带表达式的Django聚合查询

2024-04-26 11:08:20 发布

您现在位置:Python中文网/ 问答频道 /正文

我有一个模型XYZ,我需要得到给定queryset的字段a、b和表达式x/y的最大值。

它在田地里很管用。类似于:

>>> XYZ.all().aggregate(Max('a'))

... {'a__max': 10}

但是,我找不到一种方法来处理表达式。尝试类似于:

>>> XYZ.all().aggregate(Max('x/y'))

出现错误:

*** FieldError: Cannot resolve keyword 'x/y' into field. Choices are: a, b, x, y, id

尝试类似于:

>>> XYZ.all().aggregate(Max(F('x')/F('y')))

出现错误:

*** AttributeError: 'ExpressionNode' object has no attribute 'split'

甚至像这样:

XYZ.all().extra(select={'z':'x/y'}).aggregate(Max('z'))

也不起作用,并产生与上述相同的错误:

FieldError: Cannot resolve keyword 'z' into field. Choices are: a, b, x, y, id

我发现其中一个黑客是:

XYZ.all().extra(select={'z':'MAX(x/y)'})[0].z

这实际上是有效的,因为它生成了正确的SQL,但它让人困惑,因为我确实在z att属性处得到了正确的值,但没有得到正确的实例,即具有最大值的实例。

当然,我也可以使用带extra()和order_by()的原始查询或技巧,但Django一直都很好地支持聚合查询,但即使使用自己的F表达式也不支持表达式,这对我来说真的没有意义。

有办法吗?


Tags: field表达式错误allextrakeywordaremax
3条回答

对于低于1.8的版本,您可以通过这种(未记录的)方式实现相同的功能。

Book.objects.all().aggregate(price_per_page=Sum('price_per_page', 
                                                field='book_price/book_pages'))

这对Postgres有效,我不知道MySQL。

来源:Django Aggregation: Summation of Multiplication of two fields

使用F()对象的示例应该可以正常工作,因为Django 1.8:

XYZ.all().aggregate(Max(F('x')/F('y')))

有一个片段演示了在Django aggregation cheat sheet中与Sum()F()对象的聚合:

Book.objects.all().aggregate(price_per_page=Sum(F('price')/F('pages'))

在SQL中,您需要的实际上是

SELECT x/y, * FROM XYZ ORDER BY x/y DESC LIMIT 1;
# Or more verbose version of the #1
SELECT x/y, id, a, b, x, y FROM XYZ GROUP BY x/y, id, a, b, x, y ORDER BY x/y DESC LIMIT 1;
# Or
SELECT * FROM XYZ WHERE x/y = (SELECT MAX(x/y) FROM XYZ) LIMIT 1;

因此在Django ORM:

XYZ.objects.extra(select={'z':'x/y'}).order_by('-z')[0]
# Or
XYZ.objects.extra(select={'z':'x/y'}).annotate().order_by('-z')[0]
# Or x/y=z => x=y*z
XYZ.objects.filter(x=models.F('y') * XYZ.objects.extra(select={'z':'MAX(x/y)'})[0].z)[0]

版本

XYZ.all().extra(select={'z':'MAX(x/y)'})[0].z

没有正确的x、y和实例,因为MAX函数是在所有行之间求值的,当没有GROUP BY时,因此返回的QuerySet中的所有实例都将具有与MAX(x/y)相同的z值。

相关问题 更多 >