2016-08-19 70 views
0

這裏是我的模型:Django的查詢集在多個外鍵使用prefetch_related

class Owner(): 
    last_name = models.CharField(max_length=30) 
    ... 

class Species(): 
    species_code = models.CharField(max_length=10) 
    ... 

class Pet(): 
    client = models.ForeignKey(Owner, related_name='pet_fk') 
    species = models.ForeignKey(Species) 
    .... 

我想列出所有業主和他們的寵物。有些業主沒有寵物,其他人有很多。

如果寵物被發現我想要一個額外的「臨時」字段css_species_class註釋到動物的對象。如果寵物模型的species_code爲'CANINE',則該字段將返回'dog',如果'EQUINE'則返回'horse'等。

由於該網站爲多語言,因此需要「臨時」字段並且值爲css_species_class需要拉入模板中適當的字形圖標。我無法直接使用存儲的值,因此我需要插入特定的值以匹配字形所需的值。

喜歡的東西:

Owner: John Smith 
Pet: Saag (css_species_class='dog') 
Pet: Brinjal (css_species_class='cat') 
Pet: Baji (css_species_class='dog') 

Owner: Sue Smith 
Pet: none 

Owner: Clare Smith 
Pet: Aloo (css_species_class='horse') 

我的模板是這樣的:

{% for owner in owners %} 
    <tr> 
     <td>{{ owner.first_name }} {{ owner.last_name }}</td> 
     <td> <!-- loop over pet objects --> 
      {% for pet in owner.pet_fk.all %} 
       <div> 
        .... 
        <span class="glyphicons glyphicons-{{ pet.css_species_class }}"></span> 
        .... 
       </div> 
      {% endfor pet %} 
     </td> 
    </tr> 
{% endfor %} 

所以,這是我首次嘗試在一個解決方案:

class OwnerListView(ListView): 
    template_name = 'visitors/owner_list.html' 
    context_object_name = 'owners' 
    paginate_by = 50 

    def get_queryset(self): 
     owners_with_pets = Owner.objects.filter(pet_fk__isnull=False).prefetch_related('pet_fk').distinct() 
      # logic goes here to loop over pets 
      # and assign 'css_species_class' temp field 

     owners_without_pets = Owner.objects.filter(pet_fk__isnull=True).prefetch_related('pet_fk').distinct() 

然後 '合併' 的兩個查詢集合在一起:

 result_list = sorted(
      chain(owners_with_pets, owners_without_pets), 
      key=attrgetter('last_name')) 
      return result_list 

這個「作品」的少數業主,但如果我用實數測試(約4000),我得到「太多的SQL變量」的錯誤。

我本來想在一個單獨的查詢做這個(決定其拆分成兩個查詢之前),但壯觀失敗以及較大的客戶數目。

可能有人請給我一些指導,以如何更好處理這個?非常感謝。

回答

0

試試這個代碼,沒有測試,但我認爲它的工作原理。

class Owner(): 
    last_name = models.CharField(max_length=30) 
    ... 

class Species(): 
    species_code = models.CharField(max_length=10) 
    ... 

class Pet(): 
    # related_name is used for backward relation 
    # this will end up as owner."related_name" --> owner.pets 
    client = models.ForeignKey(Owner, related_name='pets') 
    species = models.ForeignKey(Species) 
    ... 

    @property 
    def css_species_class(self): 
     # this could be anything you want eg: css_scecies_class 
     return self.species.species_code 


class OwnerListView(ListView): 
    template_name = 'visitors/owner_list.html' 
    context_object_name = 'owners' 
    paginate_by = 50 

    def get_queryset(self): 
     # no need to check if onwner instance has pets if you chain them both back 
     return Owner.objects.prefetch_related('pets').all().distinct() 

{% for owner in owners %} 
    <tr> 
     <td>{{ owner.first_name }} {{ owner.last_name }}</td> 
     <td> <!-- loop over pet objects --> 
      {% for pet in owner.pets %} 
       <div> 
        .... 
        <!-- now we can access pet.css_species_class directly because we made it a property of pet class --> 
        <span class="glyphicons glyphicons-{{ pet.css_species_class }}"></span> 
        .... 
       </div> 
      {% endfor pet %} 
     </td> 
    </tr> 
{% endfor %} 
+0

它的工作精美,非常感謝你。 –