【问题标题】:Django queryset over multiple foreign keys using prefetch_related使用 prefetch_related 对多个外键进行 Django 查询集
【发布时间】:2016-12-26 14:39:45
【问题描述】:

这是我的模型:

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

这对少数所有者“有效”,但如果我使用实数(约 4,000)进行测试,我会收到“sql 变量太多”错误。

我最初尝试在单个查询中执行此操作(在决定将其分解为两个查询之前),但对于大量客户来说同样失败了。

有人可以就如何最好地解决这个问题给我一些指导吗?非常感谢。

【问题讨论】:

    标签: django python-3.x django-views django-queryset


    【解决方案1】:

    试试这个代码,没有经过测试,但我认为它可以工作。

    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 %}
    

    【讨论】:

      猜你喜欢
      • 2019-03-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-03-16
      • 2019-06-12
      • 1970-01-01
      • 2018-03-23
      • 2012-05-22
      相关资源
      最近更新 更多