这是我目前最喜欢使用的一项前沿功能,并且很快就会消失。我想将子查询聚合注释到现有查询集上。在1.11之前执行此操作意味着自定义SQL或修改数据库。这是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))
他们在总体上进行注释,这对我来说似乎很奇怪,但是无论如何。
我正在为此而苦苦挣扎,所以我将其沸腾回到我拥有数据的最简单的真实示例中。我有Carpark,其中包含许多Space。使用Book→Authorif会使你更快乐,但是-暂时-我仅想使用Subquery* 注释相关模型的数量。
Carpark
Book→Authorif
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: more than one row returned by a subquery used as an expression并且在我的脑海中,这个错误是很合理的。子查询返回带注释的总数的空格列表。
ProgrammingError: more than one row returned by a subquery used as an expression
该示例建议发生某种魔术,最后我得到一个可以使用的数字。但这不是在这里发生吗?如何注释汇总的子查询数据?
嗯,正在向查询的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"。由于某种原因,它们都在重新调整。调查仍在继续。
GROUP BY ... U0."space"
编辑2:好吧,只要查看子查询SQL,通过through我就可以看到第二组
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一个错误,但短于这两种模式移除元ORDER_BY的,有没有什么办法可以不排序在querytime查询?
也可以创建的子类Subquery,以更改其输出的SQL。例如,你可以使用:
Subquery
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中)与一系列聚合函数结合使用:我经常使用它来构建值数组或求和。
我只需要从模型中删除规定的元顺序。你可以通过.order_by()在子查询中添加一个空白来实现。在我的代码中,这意味着:
.order_by(
spaces = Space.objects.filter(carpark=OuterRef('pk')).order_by().values('carpark') count_spaces = spaces.annotate(c=Count('*')).values('c') Carpark.objects.annotate(space_count=Subquery(count_spaces))