Cha*_*tha 13 django annotate django-queryset
在下面的设置中,我想要一个带有项目列表的QuerySet,每个项目都注释了所有任务持续时间(作为tasks_duration)的总和以及所有任务的子任务持续时间的总和(asasasks_duration).我的模型(简化)看起来像这样:
class Project(models.Model):
pass
class Task(models.Model):
project = models.ForeignKey(Project)
duration = models.IntegerField(blank=True, null=True)
class SubTask(models.Model):
task = models.ForeignKey(Task)
duration = models.IntegerField(blank=True, null=True)
Run Code Online (Sandbox Code Playgroud)
我像这样制作我的QuerySet:
Projects.objects.annotate(tasks_duration=Sum('task__duration'), subtasks_duration=Sum('task__subtask__duration'))
Run Code Online (Sandbox Code Playgroud)
与Django annotate()中解释的行为相关的多次导致错误的答案我得到的task_duration比它应该高得多.多个annotate(Sum())子句在结果SQL中产生多个左内连接.对于tasks_duration只有一个注释(Sum())项,结果是正确的.但是,我想同时拥有tasks_duration和subtasks_duration.
什么是适合这种查询的方法?我有一个工作解决方案,按项目执行,但预计会非常缓慢.我也有类似的工作与extra()调用,但我真的想知道我想要的纯粹Django是否可能.
这里报告了这个bug ,但即使在Django 1.11中也没有解决.该问题与以反向关系连接两个表有关.请注意,distinct参数适用于Count但不适用于Sum.所以你可以使用技巧并编写如下的ORM:
Projects.objects.annotate(
temp_tasks_duration=Sum('task__duration'),
temp_subtasks_duration=Sum('task__subtask__duration'),
tasks_count=Count('task'),
tasks_count_distinct=Count('task', distinct=True),
task_subtasks_count=Count('task__subtask'),
task_subtasks_count_distinct=Count('task__subtask', distinct=True),
).annotate(
tasks_duration=F('temp_tasks_duration')*F('tasks_count_distinct')/F('tasks_count'),
subtasks_duration=F('temp_subtasks_duration')*F('subtasks_count_distinct')/F('subtasks_count'),
)
Run Code Online (Sandbox Code Playgroud)
更新: 我发现你需要使用子查询.在下面的解决方案,首先您筛选相关任务的outerref(OuterRef对外部查询的引用,因此任务被过滤为每个项目),那么你组的任务由"项目",使得总量适用于所有的如果项目存在任何任务,则每个项目的任务只返回一个结果(您已按'项目'过滤,然后按相同的字段分组;这就是为什么只有一个组可以在那里.)或否则为无.如果项目没有任务,结果将为None,这意味着我们不能使用[0]来选择计算的总和.
from django.db.models import Subquery, OuterRef
Projects.objects.annotate(
tasks_duration=Subquery(
Task.objects.filter(
project=OuterRef('pk')
).values(
'project'
).annotate(
the_sum=Sum('task__duration'),
).values('the_sum')[:1]
),
subtasks_duration=Sum('task__subtask__duration')
)
Run Code Online (Sandbox Code Playgroud)
运行此代码只会向数据库发送一个查询,因此性能非常好.
我也收到这个错误。完全相同的代码。如果我单独进行聚合,它是有效的,但一旦我尝试同时获得两个总和,其中一个的总和会高出 2 倍,另一个则高出 3 倍。
我不知道为什么 Django 会有这样的行为。我在这里提交了一份错误报告: https: //code.djangoproject.com/ticket/19011 您可能也有兴趣关注它。