如何根据另一个查询集计算多对多对象的相关计数?

时间:2019-09-23 19:35:30

标签: django django-orm

class Zone(Model):
  ...

class Flight(Model):
  zones = ManyToManyField(Zone)

flights = Flight.objects.filter(...)
qs1 = Zone.objects.annotate(
   count=flights.filter(zones__pk=F('pk')).distinct().count(),  # this is not valid expression
)

尽管在查询集中有F且注释中有count(),它仍然抛出错误TypeError: QuerySet.annotate() received non-expression(s): 0.,这意味着该查询集已就地执行。

也不起作用,但是这次它只返回无效值(总是1,总是计数Zone单个对象而不是内部过滤器):

qs1 = Zone.objects.annotate(
   count=Count('pk', filter=flights.filter(zones__pk=F('pk'))),  # with 'flight' instead of first 'pk' it also doesn't work
)

1 个答案:

答案 0 :(得分:1)

在Django中对.count()的评估非常迫切,因此Django将尝试对flights.filter(zones__pk=F('pk')).distinct().count()进行评估,并成功进行评估,因为F('pk')将计算fligts的数量,其中有zones的主键与Flight的主键相同。您将需要在子查询上使用OuterRef [Django-doc].annotate(..)

但是,这使它变得太复杂了。您可以简单地使用以下注释:

from django.db.models import Q, Sum

Zone.objects.annotate(
    count=Count('flight', distinct=True, filter=Q(flight__…))
)

filter=Q(flight__…)是您的排期过滤器的一部分。因此,如果Flight被假设的active=True过滤,则可以使用:

Zone.objects.annotate(
    count=Count('flight', distinct=True, filter=Q(flight__active=True))
)