2012-11-29 31 views
5

Ben Aşağıdaki modeller:önlenmesi O (n) sorgular

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 ve Group bir ara modeli Membership ile bağlantılıdır. Sanatçılar sadece is_primary, onaylanmış vb. Ile işaretlenen bir birincil gruba sahip olabilirler.

Sanatçıları listelediğim bir şablonda, temel sanatçı bilgilerini, yukarıdaki yönteme göre adlandırılan birincil gruplarına ek olarak listeliyorum. Ancak, bu bir O (n) operasyonu ve bunu yapmak için yaklaşık 160 sanatçı var. aşağıdaki gibi django-debug-toolbar sağlayan SQL geçerli:

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) 

beni bu listede her sanatçı için olur ekleyelim, bu yüzden bunlardan 160 hakkında olsun.

Model metodunu çağırdığım için O (n) yapılabilecek en iyi şey midir? Ya da bunu geliştirmek için yapabileceğim başka bir şey var mı (primary_group'u denormalize ediyorsun)? Bu, kaynak veya hedeften aramak istediğim bir aracı modelde saklanan her türlü bilgiyle ilgili bir sorun gibi görünüyor.

cevap

6

Kolayca iki sorgular, herhangi nefret edenler ne der rağmen ile yapabilirsiniz, hiç önemi yok:

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) 

(ekstra fıkra doğru olmayabilir, ancak fikir) buna ek olarak

, şöyle yapmak birincil işlevini değiştirecek:

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

ve sonra birleştirmiş o değişkene, bilgiler ekler ve sadece aynı işlevi ca kullanırsanız ll.

(Çeşitli/tuhaf sorgular katılır için Disqus biryere desen bu tür izleyin)

+1

Oldukça doğru olduğundan emin olun şimdi budur :) –

0

Bunun yerine Sanatçısı daha üyeliğiyle sorgu başlayan denediniz mi? David Cramer gibi yapardı

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

Ortaya çıkan sorgu aynıdır. –

4

ancak bunun yerine ekstra diyor: kolayca sanatçıların herhangi listeye uygulayabilirsiniz böylece bonus puan için

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) 

üyeliklerinin yöneticisi bu yöntem olun!

1

membership (artist_id,) iki sütunlu bir dizin oluşturmaya ne dersin? If you've already upgraded to 1.5b1 modelinizde bunu yapabilirsin, ama eğer yapmazsan, arka ucunda bunu yapmaktan hiçbir şey durmuyor. Bu üyelik arayışını sabit zamana indirgemelidir. DB'niz destekliyorsa, bunu partial index yapabilir, ancak sadece 160 sanatçıyla, bu gerekli görünmüyor.

İlgili konular