查询集排序:为Django ORM查询指定列排序规则

6
我开始调查为什么我的Django Model.objects.filter(condition = variable).order_by(textcolumn)查询不能按正确顺序返回对象。后来发现这是数据库(Postgresql)的问题。
在我之前的问题中(Postgresql sorting language specific characters (collation)),我找出了(在zero323的大力帮助下实际上让它工作)我可以像这样指定每个数据库查询的排序规则:
SELECT nimi COLLATE "et_EE" FROM test ORDER BY nimi ASC;

但据我所知,order_by只接受字段名作为参数。
我在想,是否有可能扩展该功能以包括collation参数?是否可以使用mixin或其他方法进行黑客攻击?或者现在唯一的方法是提出功能请求?
我希望它能像这样工作:
Model.objects.filter(condition = variable).order_by(*fieldnames, collation = 'et_EE')

编辑1: 显然,我不是唯一一个提出这个要求的人: https://groups.google.com/forum/#!msg/django-developers/0iESVnawNAY/JefMfAm7nQMJ

艾伦


1
要提出改进Django的建议,请提交一个新功能请求,或在django-developers上发布帖子。目前,您可能需要使用原始SQL查询 - Kevin Christopher Henry
嗯,好的。我想我会请求那个功能。但我首先想知道是否可以使用现有的工具来实现它。原始查询肯定是其中之一。 - Odif Yltsaeb
3个回答

12

正如@olau在评论中提到的那样,自Django 3.2起,Collate实用程序可用。对于旧版本的Django,请参阅以下代码示例下方的原始信息:

# Starting with Django 3.2:
Test.objects.order_by(Collate('nimi', 'et_EE'))

自Django 1.8开始,order_by()不仅接受字段名还接受查询表达式
另一个答案中,我举了一个如何覆盖列的默认排序规则的示例。这里有一个有用的查询表达式Func(),你可以继承或直接使用它:
nimi_et = Func(
    'nimi',
    function='et_EE',
    template='(%(expressions)s) COLLATE "%(function)s"')
Test.objects.order_by(nimi_et.asc())

请注意,生成的SQL将更像:

SELECT nimi FROM test ORDER BY nimi COLLATE "et_EE" ASC;

也就是说,在ORDER BY子句中覆盖了排序规则,而不是在SELECT子句中。但是,如果您需要在WHERE子句中使用它,可以在annotate()中使用Func()

1
请注意,在 ORDER BY 中设置排序规则会阻止使用本来适用的任何索引。 - Rick James
哇!这是在Django中支持i18n的最重要的答案,可是没人知道,也没人关心吗?非常感谢! - mirek
1
Django 3.2发布了一个Collate函数,可以实现这个功能。 - olau

2

好的。目前似乎原始查询是实现此操作的唯一方法。

但是,有一个Django 处于打开状态,希望能尽快关闭/解决。


0
对我来说有效的方法是:
from django.db.models.functions import Collate

Model.objects.filter(condition = variable).order_by(Collate("field_name", "C"))

在PostgreSQL的上下文中,COLLATE "C"表示“默认”或“二进制排序”。这意味着字符串按字节进行比较,不考虑区域设置和排序规则。在“C”区域设置中,比较发生在最低级别,这在比较二进制数据时非常有用,因为字符串内的字节顺序很重要。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接