当前位置 : 主页 > 编程语言 > python >

Django 1.11注释子查询聚合

来源:互联网 收集:自由互联 发布时间:2021-06-25
这是一个前沿的功能,我目前正在解决这个问题并迅速流血.我想在现有的查询集上注释子查询聚合.在1.11之前执行此操作要么意味着自定义SQL,要么锤击数据库. Here’s the documentation for t
这是一个前沿的功能,我目前正在解决这个问题并迅速流血.我想在现有的查询集上注释子查询聚合.在1.11之前执行此操作要么意味着自定义SQL,要么锤击数据库. Here’s the documentation for this,以及它的例子:

from django.db.models import OuterRef, Subquery, Sum
comments = Comment.objects.filter(post=OuterRef('pk')).values('post')
total_comments = comments.annotate(total=Sum('length')).values('total')
Post.objects.filter(length__gt=Subquery(total_comments))

他们在总体上注释,这对我来说似乎很奇怪,但无论如何.

我正在努力解决这个问题,所以我正在把它煮回来,回到我有数据的最简单的现实世界的例子.我有停车场,其中包含许多空间.使用Book→Author如果这让你更快乐,但是 – 现在 – 我只想使用Subquery *来注释相关模型的计数.

spaces = Space.objects.filter(carpark=OuterRef('pk')).values('carpark')
count_spaces = spaces.annotate(c=Count('*')).values('c')
Carpark.objects.annotate(space_count=Subquery(count_spaces))

这给了我一个可爱的ProgrammingError:用作表达式的子查询返回的多行,在我的脑海中,这个错误非常有意义.子查询返回带有注释总计的空格列表.

这个例子表明会发生某种魔法,我最终会得到一个我可以使用的数字.但这不是在这里发生的?如何对聚合子查询数据进行注释?

嗯,有什么东西被添加到我的查询的SQL中……

我建造了一个新的停车场/太空模型,它的工作原理.所以下一步是弄清楚我的SQL中毒了什么.根据Laurent的建议,我看了一下SQL并尝试使它更像是他们在答案中发布的版本.这就是我发现真正问题的地方:

SELECT "bookings_carpark".*, (SELECT COUNT(U0."id") AS "c"
FROM "bookings_space" U0
WHERE U0."carpark_id" = ("bookings_carpark"."id")
GROUP BY U0."carpark_id", U0."space"
)
AS "space_count" FROM "bookings_carpark";

我突出了它,但它是子查询的GROUP BY … U0.“space”.由于某种原因,它正在重新调整.调查仍在继续.

编辑2:好的,只是查看子查询SQL我可以通过☹看到第二组

In [12]: print(Space.objects_standard.filter().values('carpark').annotate(c=Count('*')).values('c').query)
SELECT COUNT(*) AS "c" FROM "bookings_space" GROUP BY "bookings_space"."carpark_id", "bookings_space"."space" ORDER BY "bookings_space"."carpark_id" ASC, "bookings_space"."space" ASC

编辑3:好的!这两个模型都有排序顺序.这些正在传递到子查询.正是这些订单膨胀了我的查询并打破了它.

我想这可能是Django中的一个错误但是在这两个模型上删除了Meta-order_by,有没有什么方法可以在查询时解除查询?

*我知道我可以为这个例子注释一个Count.我使用它的真正目的是一个更复杂的过滤器计数,但我甚至无法实现这一点.

也可以创建Subquery的子类,以更改它输出的SQL.例如,您可以使用:

class SQCount(Subquery):
    template = "(SELECT count(*) FROM (%(subquery)s) _count)"
    output_field = models.IntegerField()

然后,就像使用原始Subquery类一样使用它:

spaces = Space.objects.filter(carpark=OuterRef('pk')).values('pk')
Carpark.objects.annotate(space_count=SQCount(spaces))

你可以使用这个技巧(至少在postgres中)使用一系列聚合函数:我经常使用它来构建一个值数组,或者总结它们.

网友评论