【问题标题】:Aggregrate query with related models, not just related ID values使用相关模型聚合查询,而不仅仅是相关的 ID 值
【发布时间】:2015-05-08 00:18:09
【问题描述】:

假设我有三个模型:

from django.db import models    

class X(models.Model):
    y = models.ForeignKey(Y, on_delete=models.PROTECT)
    z = models.ForeignKey(Z, on_delete=models.PROTECT)
    a = models.DecimalField(...)
    b = models.DecimalField(...)
    ...some fields...

class Y(models.Model):
   ...some fields...

class Z(models.Model):
   ...some fields...

我想使用 ORM 使用 X 模型进行聚合查询,但我不想按 X.id 分组(我想按 X.a 分组)并且我希望生成的对象具有 Y和 Z 相关模型预先填充在其中(以避免重复查询相关数据)。

据我所知,Django 的聚合查询只允许您按模型的 ID 分组(在我看来有点没用),或者按另一个字段分组,然后将结果折叠到单个记录/对象中。

没有中间立场允许您按模型 ID 以外的字段进行分组,并且不会将结果折叠到单个记录/对象中。

我意识到这很可能是因为这种中间立场不适合模型的概念(因为如果您将多个模型记录组合在一起,它们真的不再是模型实例了),但如果能够利用 ORM 的力量并达到这个中间立场。

目前我不得不回避 ORM 并推出我自己的自定义模型实现并使用原始查询,但这有点笨拙,我希望随着 Django 1.8 的发布以及我可能能够返回的新查询功能ORM,但似乎并非如此。

我能得到的最接近的是这样的:

X.objects.all().values('a', 'y', 'z').annotate(temp=Sum('b'))

...翻译成这个 SQL:

SELECT app_x.a, app_x.y_id, app_x.z_id, SUM(app_x.b) AS temp
FROM app_x LEFT OUTER JOIN app_y ON (app_x.y_id = app_y.id)
GROUP BY app_x.a, app_x.y_id, app_x.z_id

我想从 ORM 得到的 SQL 是这样的:

SELECT app_x.a, app_y.*, app_z.*, SUM(app_x.b) AS temp
FROM app_x LEFT OUTER JOIN app_y ON (app_x.y_id = app_y.id)
   LEFT OUTER JOIN app_z ON (app_x.z_id = app_z.id)
GROUP BY app_x.a, app_y.*, app_z.*

...这样我不仅可以获取相关的 ID,还可以获取完整的模型实例,可以立即使用并避免将来的查询。

ORM 允许这样做吗?

我能看到的唯一解决方案是在 .values() 调用中手动定义相关模型中的所有字段,但这不会返回查询集,而且必须手动列出所有必要的相关信息有点乏味字段(而不是仅仅进行 .select_related() 调用)。

编辑 1

如果 .values() 调用返回实际相关的模型对象而不仅仅是它们的 ID 值,这将很容易。

即我认为这样的查询:

X.objects.all.values('y')

...应该返回包含 Y 个对象而不是 Y 个 ID 的字典列表。

这样的查询:

X.objects.all.values('y_id')

...应该返回包含 ID 的字典列表,因为它是您要求的。

有人同意我的观点吗?

【问题讨论】:

    标签: django orm model aggregate-functions


    【解决方案1】:

    X.objects.annotate(temp=Sum('b')).select_related('y','z') 适合你吗?

    select_related docs

    编辑:刚刚看到您在问题中排除了这一点。我没有尝试过,但values_list() 是否可以返回外键字段的模型实例?如果没有,并且您使用 values() 的原因是模型 X 上有很多不需要的字段,也许可以考虑使用 defer/only

    【讨论】:

    • 是和不是。是的,它确实返回了相关的模型对象,但没有,因为它按“id”对 X 进行分组,我想避免这种情况。使用 .values() 似乎是避免按模型的“id”字段分组的唯一方法。
    • 查看我的编辑。如果做不到这一点,您也可以退回到对 Y 和 Z 模型进行过滤,例如related_zs = Z.objects.filter(x_set__in=x_values)。这是 2 个额外的查询,但您可以根据需要对它们进行分组。
    • .values_list() 只返回列表列表,而不是字典列表。您的相关解决方案看起来确实合理,但我想知道是否有一种方法可以在一个查询中完成所有操作。如果没有,那么我会接受你的建议。
    猜你喜欢
    • 2019-04-17
    • 1970-01-01
    • 2017-04-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-12-02
    • 2013-02-07
    相关资源
    最近更新 更多