在下面的设置,我想有一个项目清单,各有其所有任务持续时间的总和(如tasks_duration)注释一个QuerySet及其所有任务的子任务持续时间的总和(如subtasks_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的注释()多次导致错误的答案 ,我收到了tasks_duration是远高于它应该是。 所述多个注释(萨姆())的条款得到在所得SQL多个左内联接。 只有一个单一的注释(总和()),期限为tasks_duration,结果是正确的。 不过,我想有两个tasks_duration和subtasks_duration。
什么是做这个查询一个合适的方式? 我有一个工作的解决方案,它是每个项目,但是这果然unusably缓慢。 我也有类似的一个额外的()调用工作的东西,但我真的很想知道,如果我要的是可以用纯Django的。
该错误报道这里 ,但它甚至没有在Django 1.11尚未解决。 该问题涉及到反向关系连接两个表。 请注意,不同的参数可以很好地用于计数,但不和。 所以,你可以使用一个技巧,写一个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'),
)
更新:我发现,你需要使用子查询。 在下面的解决方案,首先您筛选相关任务的outerref(OuterRef对外部查询的引用,因此任务被过滤为每个项目),那么你组的任务由“项目”,使得总量适用于所有的每个项目和回报,如果任何任务存在的项目只是一个结果的任务(您已通过“项目”过滤,然后由同一领域进行分组;这就是为什么只有一个组可以有。)或无其他。 其结果将是无如果项目没有任务,这意味着我们不能用[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')
)
运行该代码将只发送一个查询到数据库中,这样的表现是伟大的。
我得到这个错误也是如此。 完全相同的代码。 它的工作原理,如果我单独做了汇总,但一旦我试图在同一时间得到两个和,其中一人获得2倍以上,其他3倍。
我不知道为什么Django的行为这种方式。 我在这里提交bug报告: https://code.djangoproject.com/ticket/19011你可能有兴趣关注它。