2010-11-23 18 views
5

'Through' sınıfına sahip bir ManyToManyField kullanıyorum ve bu, bir şeyler listesi alırken birçok sorgunun sonucunu veriyor. Daha verimli bir yol var mı diye merak ediyorum. ÖrneğinDjango ManyToMany'i 'sorgular aracılığıyla daha verimli hale getirebilirim?

burada Rol sınıfına geçer Kitap ve onların çeşitli yazarlar açıklayan bazı basitleştirilmiş sınıfları vardır ("Editör", "Illustrator", vb gibi roller tanımlamasını):

class Person(models.Model): 
    first_name = models.CharField(max_length=100) 
    last_name = models.CharField(max_length=100) 

    @property 
    def full_name(self): 
     return ' '.join([self.first_name, self.last_name,]) 

class Role(models.Model): 
    name = models.CharField(max_length=50) 
    person = models.ForeignKey(Person) 
    book = models.ForeignKey(Book) 

class Book(models.Model): 
    title = models.CharField(max_length=255) 
    authors = models.ManyToManyField(Person, through='Role') 

    @property 
    def authors_names(self): 
     names = [] 
     for role in self.role_set.all(): 
      person_name = role.person.full_name 
      if role.name: 
       person_name += ' (%s)' % (role.name,) 
      names.append(person_name) 
     return ', '.join(names) 

ben ararsam Book.authors_names() daha sonra böyle bir dize şey elde edebilirsiniz:

John Doe (Editör), Fred Bloggs, Billy Bob (Illustrator)

İyi çalışıyor ama kitap için Rolleri almak için bir sorgu ve ardından her Kişi için başka bir sorgu yapıyor. Kitapların bir listesini görüntülüyorsam, bu bir çok soruyu ekler.

Bunu daha verimli bir şekilde, Kitap başına tek bir sorguda, birleştirme ile yapmanın bir yolu var mı? Veya batch-select gibi bir şeyi kullanmanın tek yolu nedir?

(bonus puan için ... authors_names benim kodlama() biraz aksak görünüyor? - daha zarif Python vari yapmak için bir yol yoktur)

+2

'Python vari' genellikle Monty Python için karşılaştırmalar için ayrılmıştır: Aradığınız kelime 'Pythonic' dir. –

+1

@daniel: 'pythonic' doğru kullanımı için +1, 'python-esque' kullanımının yazarın kodu biraz daha eğlenceli hale getirmek istediğini ima edebilir ... –

+3

Düzeltme için teşekkürler. Ancak, bundan sonra kodumu sadece daha doğru değil aynı zamanda daha eğlenceli hale getirmek için çaba göstereceğim. –

cevap

8

Bu Django genellikle rastlamak bir kalıptır. author_name gibi özellikler oluşturmak gerçekten çok kolay ve bir kitap görüntülediğinizde harika çalışıyorlar, ancak bir sayfada birçok kitap için mülkü kullanmak istediğinizde sorgu sayısı patlar.

Öncelikle, Ancak bu her kitap için rol aranırken sorununu çözmez herkesin

for role in self.role_set.all().select_related(depth=1): 
     person_name = role.person.full_name 
     if role.name: 
      person_name += ' (%s)' % (role.name,) 
     names.append(person_name) 
    return ', '.join(names) 

için arama önlemek için select_related kullanabilirsiniz.

Kitapların bir listesini görüntülüyorsanız, kitaplarınızın tüm rollerini bir sorguda arayabilir, sonra önbelleğe alabilirsiniz.

>>> books = Book.objects.filter(**your_kwargs) 
>>> roles = Role.objects.filter(book_in=books).select_related(depth=1) 
>>> roles_by_book = defaultdict(list) 
>>> for role in roles: 
... roles_by_book[role.book].append(books)  

Ardından roles_by_dict sözlükten bir kitabın rolleri erişebilir.

>>> for book in books: 
... book_roles = roles_by_book[book] 

Böyle önbelleğe kullanmak için author_name özelliği gözden geçirmek zorunda kalacaklardır.


Ayrıca bonus puanları için çekerim.

Tam adı ve rol adını oluşturmak için role bir yöntem ekleyin.

class Role(models.Model): 
    ... 
    @property 
    def name_and_role(self): 
     out = self.person.full_name 
     if self.name: 
      out += ' (%s)' % role.name 
     return out 

Paulo'nun önerisine benzer bir astar için author_names çökmeler

@property 
def authors_names(self): 
    return ', '.join([role.name_and_role for role in self.role_set.all() ]) 
+0

Ah, select_related() işaretçisi Alasdair için teşekkürler, bu bir gelişme. Cevabınızın ikinci bölümünü kodumda nasıl kullanacağımı düşünmek zorundayım. Belki özel bir müdürde? –

+0

Daha önce parti seçimlerine rastlamadım, ama umut verici görünüyor. Kendi özel yöneticimi yazmaya çalışmadan önce bunu araştırabilirim. – Alasdair

+0

Role .__ unicode__, oluşturma için ideal bir adaydır –

1

Ben Rolü de authors = models.ManyToManyField(Role) ve mağaza tamadı hale getirecektir. takma ad, aynı kişi farklı takma adlar altında kitap imzalayabilir. aksak Hakkında

bu:

def authors_names(self): 
    names = [] 
    for role in self.role_set.all(): 
     person_name = role.person.full_name 
     if role.name: 
      person_name += ' (%s)' % (role.name,) 
     names.append(person_name) 
    return ', '.join(names) 

olabilir:

def authors_names(self): 
    return ', '.join([ '%s (%s)' % (role.person.full_name, role.name) 
       for role in self.role_set.all() ]) 
+0

Sahte dürüstlüklerin dürüst olması konusunda endişelenmiyorum - bu projenin amaçları için, bir yazarın adı, takma ad veya kişi değil. –

+0

Ve kod önerisi için teşekkürler. Ama bu tam olarak aynı şeyi yapmaz - eğer herhangi bir role.name yoksa yazarın adından sonra boş parantez istemiyorum. –

İlgili konular