2016-08-19 56 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

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