2012-11-29 11 views
5

मैं निम्नलिखित मॉडल:रोकथाम हे (एन) मध्यस्थ मॉडल वाली क्वेरी

class Artist(models.Model): 
    name = models.CharField() 

    def primary_group(self): 
     return self.memberships.select_related('group').get(is_primary=True) 

class Group(models.Model): 
    name = models.CharField() 
    members = models.ManyToManyField(Artist, through='Membership') 

class Membership(models.Model): 
    artist = models.ForeignKey(Artist, related_name='memberships') 
    group = models.ForeignKey(Group) 
    is_primary = models.BooleanField() 

Artist और Group एक मध्यस्थ के मॉडल, Membership के माध्यम से जुड़े हुए हैं। कलाकारों के पास केवल एक प्राथमिक समूह हो सकता है, जिसे is_primary, मान्य, आदि के माध्यम से चिह्नित किया गया है।

एक टेम्पलेट में जहां मैं कलाकारों को सूचीबद्ध करता हूं, मैं उपरोक्त विधि द्वारा बुलाए गए अपने प्राथमिक समूह के अलावा मूल कलाकार जानकारी सूचीबद्ध करता हूं। हालांकि, यह एक ओ (एन) ऑपरेशन है और मेरे पास ऐसा करने के लिए 160 कलाकार हैं। एसक्यूएल कि Django-डिबग-उपकरण पट्टी प्रदान करता है इस प्रकार है:

SELECT ••• FROM "people_membership" 
      LEFT OUTER JOIN "people_group" ON ("people_membership"."group_id" = "people_group"."id") 
      WHERE ("people_membership"."artist_id" = xx AND "people_membership"."is_primary" = true) 

मुझे जोड़ने कि इस सूचीबद्ध हर कलाकार के लिए होता है, तो मैं इनमें से 160 के बारे में मिलता है।

क्या ओ (एन) सबसे अच्छा किया जा सकता है, क्योंकि मैं मॉडल विधि कहता हूं? या क्या ऐसा कुछ और है जो मैं इसे सुधारने के लिए कर सकता हूं (primary_group denormalizing से कम)? यह किसी भी प्रकार की जानकारी के साथ एक समस्या है जो एक मध्यस्थ मॉडल में संग्रहीत है जिसे मैं स्रोत या लक्ष्य से कॉल करना चाहता हूं।

उत्तर

6

आप आसानी से दो प्रश्नों, जो के बावजूद किसी भी नापसंद करने क्या कहेंगे साथ ऐसा कर सकते हैं, न बात बिल्कुल करता है:

artists = list(Artist.objects.all()) 
primary_memberships = {m.artist_id: m for m in Group.objects.filter(is_primary=True, membership__artist__in=artists).extra(select={'artist_id': '%s.artist_id' % (Membership._meta.db_table,)})} 
for artist in artists: 
    artist.primary_membership = primary_memberships.get(artist.id) 

(अतिरिक्त खंड सही नहीं हो सकता है, लेकिन आप अंदाजा हो)

इस के अलावा, मैं प्राथमिक कार्य बदल जाएगा की तरह करने के लिए:

if hasattr(self, '_primary_membership_cache'): 
    return self._primary_membership_cache 

और फिर आप यह बाँध कि चर जानकारी शामिल करते हैं, और सिर्फ अपने ही समारोह सीए का उपयोग करता है, तो डालूँगा।

(हम विभिन्न मिलती/अजीब प्रश्नों के लिए DISQUS पर हर जगह पैटर्न इस तरह का पालन करें)

+1

बहुत यकीन है कि यह अब ज्यादातर सही है :) –

0

आप बल्कि कलाकार की तुलना में सदस्यता के साथ क्वेरी शुरू करने की कोशिश की है?

class Artist(models.Model): 
    ... 
    def primary_group(self): 
     return Membership.objects.filter(artist=self).get(is_primary=True).group 
+0

परिणामी क्वेरी वही है। –

4

मैं इसे डेविड क्रेमर की तरह करना होगा कहते हैं, लेकिन अतिरिक्त के बजाय:

primary_memberships = {m.artist_id: m.group for m in Membership.objects.filter(group__isprimary=True, artist__in=artists).select_related('group')} 
for artist in artists: 
    artists.primary_membership = primary_memberships.get(artist.id) 

बोनस अंक के लिए यह एक विधि बनाने सदस्यता के प्रबंधक पर ताकि आप आसानी से कलाकारों में से किसी भी सूची में इसे लागू कर सकते हैं!

1

membership (artist_id, is_primary) पर दो कॉलम इंडेक्स डालने के बारे में कैसे? If you've already upgraded to 1.5b1 आप इसे अपने मॉडल के भीतर कर सकते हैं, लेकिन यदि आपने नहीं किया है तो बैकएंड पर ऐसा करने से आपको कुछ भी रोक नहीं रहा है। इससे निरंतर समय तक सदस्यता लुकअप को कम करना चाहिए। यदि आपका डीबी इसका समर्थन करता है, तो आप इसे partial index बना सकते हैं, लेकिन केवल 160 कलाकारों के साथ, यह आवश्यक नहीं लगता है।

संबंधित मुद्दे