在下面的设置中,我想要一个带有项目列表的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)
我像这样制作我的QuerySet:
Projects.objects.annotate(tasks_duration=Sum('task__duration'), subtasks_duration=Sum('task__subtask__duration'))
与Django annotate() multiple times causes wrong answers中解释的行为相关,我得到的task_duration比它应该高得多。多个annotate(Sum())子句在结果SQL中产生多个左内连接。对于tasks_duration只有一个注释(Sum())项,结果是正确的。但是,我想同时拥有tasks_duration和subtasks_duration。
进行此查询的合适方法是什么?我有一个工作解决方案,按项目执行,但预计会非常缓慢。我也有类似的工作与extra()调用,但我真的想知道我想要的纯Django是否可能。
答案 0 :(得分:6)
报告错误here,但即使在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'),
)
<强>更新强> 我发现你需要使用Subquery。在下面的解决方案中,首先过滤与outerref相关的任务(外部查询的OuterRef引用,以便为每个项目过滤任务),然后按“项目”对任务进行分组,以便Sum适用于每个项目的所有任务,如果项目存在任何任务,则只返回一个结果(您已经过&#39;项目&#39;然后按相同的字段分组;这就是为什么只有一个团体可以在那里。)或否则无。如果项目没有任务,结果将为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')
)
运行此代码只会向数据库发送一个查询,因此性能非常好。
答案 1 :(得分:1)
我也得到了这个错误。完全相同的代码。如果我单独进行聚合,它可以工作,但是一旦我尝试同时获得两个总和,其中一个得到的因子高2,另一个得到因子3。
我不知道为什么Django会这样做。我在这里提交了一份错误报告: https://code.djangoproject.com/ticket/19011 您可能也有兴趣关注它。