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

问题描述 投票:0回答:2

假设我有两个模型:

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

django django-orm
2个回答
0
投票

尝试使用annotate()。你可以注释一个字段,它保存所有minutes_plannedRecords的总和,然后使用这个值来过滤掉所需的Tasks。查询将类似于:

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

希望这可以帮助。


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),它通过在此管理器中查询的任务的pk过滤记录。

然后,minutes_planned返回将与Task的duration进行比较的实际总数。

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

Reference

© www.soinside.com 2019 - 2024. All rights reserved.