尝试在Django ORM查询中使用相关对象字段的总数

时间:2019-04-02 04:28:07

标签: django django-orm

假设我有两个模型:

class Task(Model):
    duration = models.IntegerField(default=100)

class Record(Model):
    minutes_planned = models.IntegerField(default=0)
    task = models.ForeignKey(Task, related_name=records)

我想了解所有在所有相关记录中计划的总分钟数少于对象持续时间的对象。我一直在文档中找不到解决方案。有人可以指出我的意思吗?

Task.objects.filter(duration__gt=F('records__minutes_planned')))

Task.objects.filter(duration__gt=Sum('records__minutes_planned'))

Task.objects.filter(duration__gt=Sum(F('records__minutes_planned')))

,但到目前为止没有任何效果。第一个成功运行,但是据我所知,它是将它们一个一个地而不是与所有记录的总数进行比较。

似乎Sum仅限于.aggregate()中使用。但是,我想检索对象本身,而不是一组.aggregate()会给我的值。

更新: 发现this portion the docs看起来很有希望。

2 个答案:

答案 0 :(得分:0)

尝试使用annotate()。您可以注释一个字段,其中包含所有minutes_planned的{​​{1}}的总和,然后使用该值过滤掉所需的Records。查询将类似于:

Tasks

希望这会有所帮助。

答案 1 :(得分:0)

这是写成模型管理器的最终解决方案:

from django.db.models import Manager, OuterRef, Subquery, Sum
from django.db.models.functions import Coalesce

class TaskManager(Manager):

    def eligible_for_planning(self, user):
        from .models import Record
        records = Record.objects.filter(task=OuterRef('pk')).order_by().values('task')
        minutes_planned = records.annotate(total=Sum('minutes_planned')).values('total')
        qs = self.model.objects.filter(user=user, status=ACTIONABLE, duration__gt=Coalesce(Subquery(minutes_planned), 0))
        return qs

我们基本上是在构造第二个查询,以获取第一个查询所需的值。

在这种情况下,records是第二个查询(或SubQuery),它根据在此管理器中查询的Task的pk过滤记录。

然后,minutes_planned返回将与任务的duration比较的实际总数。

最后,将整个对象作为Subquery对象插入查询集中。如果找不到Record对象,则将其包装在Coalesce中并添加默认值。就我而言,这是零。

Reference