queryset,union,order_by 在带注释的字段上

Posted

技术标签:

【中文标题】queryset,union,order_by 在带注释的字段上【英文标题】:queryset, union, order_by over an annotated field 【发布时间】:2019-05-31 03:23:48 【问题描述】:

在按字段排序时,Django 似乎有点想太多了。

我需要合并两个查询(queryset),第一个是排名,第二个不是,在最终结果中我想要一个 queryset,因为它将被分页。

我会给你一个使用User模型的例子,这样你就可以在家里尝试了。

from django.contrib.auth.models import User
from django.db.models import F, Value, IntegerField
from django.db.models.expressions import RawSQL

queryset = User.objects

a = queryset.filter(email__contains='a').annotate(rank=RawSQL("rank() OVER (ORDER BY id desc)", [], output_field=IntegerField()))
b = queryset.filter(email__contains='b').annotate(rank=Value(None, output_field=IntegerField()))

a.union(b).order_by(F('rank').desc(nulls_last=True))
# DatabaseError: ORDER BY term does not match any column in the result set.

a.order_by(F('rank').desc(nulls_last=True))
# this is OK

b.order_by(F('rank').desc(nulls_last=True))
# ProgrammingError: non-integer constant in ORDER BY
# LINE 1: ...ERE "auth_user"."email"::text LIKE '%b%' ORDER BY NULL DESC ...

这是一个 Django 错误吗?

我正在使用 Django==1.11.17

【问题讨论】:

最后一个错误来自数据库,而不是来自 Django。我们可以print(b.order_by(F('rank').desc(nulls_last=True)).query)查看这里生成的SQL。 【参考方案1】:

我认为问题的出现是因为 Django 仅通过注入 NULL 来处理 Value(None, output_field=IntegerField()),这在 ORDER BY 子句中无效。

然后,因为 Django 将被注释的表达式作为 ORDER BY 放入,所以它使用这个值。

我认为克服这个问题的唯一方法是让整个查询(即联合的两个部分)在子查询内进行评估 - 然后将有用于注释的值的列,而不仅仅是表达式自己。

也许你会争辩说这是 django 应该做的,因为它克服了这个问题(也意味着数据库不会多次评估表达式,在 SELECT 和 WHERE 部分中)。

【讨论】:

以上是关于queryset,union,order_by 在带注释的字段上的主要内容,如果未能解决你的问题,请参考以下文章

Django QuerySet 过滤器 + order_by + 限制

如何在 ModelAdmin.formfield_for_manytomany() 中使用 Django QuerySet.union()?

django queryset union 不能仅使用

Django QuerySet按表达式排序[重复]

oracle数据库,PLS-S-01705, 如果游标说明中有 UNION 或 ORDER_BY, 则由游标指定的表不可更新

查询集 QuerySet