【问题标题】:Combine prefetch_related and annotate in Django在 Django 中结合 prefetch_related 和 annotate
【发布时间】:2014-10-12 17:19:55
【问题描述】:

我有三个模型

class ModelA(models.Model):
    name = CharField(max_length=100)

class ModelB(models.Model):
    modela = ForeignKey(ModelA)

class ModelC(models.Model):
    modelb = ForeignKey(ModelB)
    amount = IntegerField()

我可以得到输出

name, number of model c objects
==============
Some name, 312
Another name, 17

使用查询集

ModelA.objects.all().prefetch_related('modelb_set', 'groupb_set__modelc_set')

和模板

{% for modela in modela_list %}
    {% for modelb in modela.modelb_set.all %}
        {{ modelb }}, {{ modelb.modelc_set.count }}
    {% endfor %}
{% endfor %}

我不想计算连接到每个 ModelB 对象的 ModelC 对象的数量,而是想对 ModelC 中的金额字段求和。

我不知道如何在我的查询集中组合 prefetch_relatedannotate,但它一定是这样的

(ModelA.objects.all()
       .prefetch_related('modelb_set', 'groupb_set__modelc_set')
       .annotate(total_amount=Sum('modelc_set__amount')))

【问题讨论】:

  • 我认为目前不可能。对于我的用例,我能够通过仅在相关集上运行 annotate 将其分解为两个单独的查询。但是,您的用例稍微复杂一些,因为您从查询集而不是单个对象开始。
  • 我认为这里没有任何必要的注释。您已经完全预取了 modelb_set 及其相关的 modelc_set。你想要的是做一个len(modelb.modelc_set.all()) 的python 实现,也许在上下文中,也许是模型上用于模板的方法。如果你做得对,这不应该触发额外的查询,因为该列表已完全加载到内存中。即使使用.count() 语法也可能不会触发查询,但这取决于 Django 中的实现。

标签: django django-models django-templates django-views django-queryset


【解决方案1】:

我认为您应该能够通过这样做来实现这一目标:

from django.db.models import F, Sum

(ModelA.objects.all()
    .prefetch_related('modelb_set', 'modelb__modelc_set')\
    .values('name')\ # group values by modela.name, read: https://docs.djangoproject.com/en/1.9/topics/db/aggregation/
    .annotate(name = F('name'),
              total_amount = Sum('modelb__modelc__amount')))

在你的模板中你应该使用:

{% for modela in modela_list %}
    {{ modela.name }}, {{ modela.total_amount }}
{% endfor %}

【讨论】:

    【解决方案2】:

    您可以将其简化为:

    from django.db.models import F, Sum
    
    ModelA.objects.all()
        .prefetch_related('modelb__modelc_set') \
        .values('name') \
        .annotate(name=F('name'), total_amount=Sum('modelb__modelc__amount'))
    

    我知道这适用于更高版本的 Django。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-09-12
      • 2016-11-28
      • 2017-10-10
      • 2012-06-10
      • 1970-01-01
      相关资源
      最近更新 更多