尝试在 Django ORM 查询中使用相关对象字段的总数
Trying to use the total of related objects' fields in a Django ORM query
假设我有两个模型:
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 看起来很有希望。
尝试使用 annotate()。您可以注释一个字段,该字段包含所有 Records
的 minutes_planned
的总和,然后使用此值过滤掉所需的 Tasks
。查询将类似于:
Task.objects.annotate(total_minutes_planned=Sum('records__minutes_planned')).
filter(duration__gt=total_minutes_planned)
希望对您有所帮助。
这是作为模型管理器编写的最终解决方案:
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
returns 将与任务的 duration
进行比较的实际总数。
最后,整个内容作为 Subquery
对象插入到查询集中。如果没有找到 Record 对象,请将其包装在 Coalesce
中并添加默认值。就我而言,这是零。
假设我有两个模型:
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 看起来很有希望。
尝试使用 annotate()。您可以注释一个字段,该字段包含所有 Records
的 minutes_planned
的总和,然后使用此值过滤掉所需的 Tasks
。查询将类似于:
Task.objects.annotate(total_minutes_planned=Sum('records__minutes_planned')).
filter(duration__gt=total_minutes_planned)
希望对您有所帮助。
这是作为模型管理器编写的最终解决方案:
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
returns 将与任务的 duration
进行比较的实际总数。
最后,整个内容作为 Subquery
对象插入到查询集中。如果没有找到 Record 对象,请将其包装在 Coalesce
中并添加默认值。就我而言,这是零。