简体   繁体   English

Django使用自定义排序规则进行查询

[英]Django making a query with custom collation

是否可以使用与数据库表不同的排序规则进行查询?

Using extra() is a little messy.使用extra()有点麻烦。 Something similar can now be achieved with Func() expression (since Django 1.8):现在可以使用Func()表达式实现类似的功能(自 Django 1.8 起):

username_ci = Func(
    'username',
    function='utf8_general_ci',
    template='(%(expressions)s) COLLATE "%(function)s"')

This can be used in annotate() :这可以在annotate()

User.objects.annotate(uname_ci=username_ci).filter(uname_ci='joeblow').exists()

Or in order_by() to override default collation rules when sorting:或者在order_by()在排序时覆盖默认的排序规则:

User.objects.order_by(username_ci)

Now, it still may seem messy, but if you look at the docs and code of Func() , you will discover that it is very easy to subclass it and make a reusable collation setter.现在,它看起来仍然很乱,但是如果您查看Func()的文档和代码,您会发现将其子类化并制作可重用的排序规则设置器非常容易。

I used this trick with Postgres database.我在 Postgres 数据库中使用了这个技巧。

Here is how you can use a specific collation instead of the default collation for a given table/column.以下是如何使用特定的排序规则而不是给定表/列的默认排序规则。 I'm assuming you always want that to be the case insensitive utf8_general_ci, but you can easily change that in the code or add it as a variable.我假设您总是希望它不区分大小写 utf8_general_ci,但是您可以轻松地在代码中更改它或将其添加为变量。

Note the use of the params kwarg instead of the db literal function.请注意使用 params kwarg 而不是 db 文字函数。 Params exists for the exact same purpose.参数存在的目的完全相同。

def iexact(**kw):
    fields = [['%s=%%s collate utf8_general_ci'%field,value] for (field,value) in kw.items()]
    return dict(where=[f[0] for f in fields], params=[f[1] for f in fields])

if User.objects.extra(**iexact(username='joeblow')).exists():
    status = "Found a user with this username!"

I solve this using bit of a hack;我用一点技巧解决了这个问题;

Django's extra method is just like raw method, they both using the query statetment directly; Django的extra方法和raw方法一样,都是直接使用查询语句;

MyModel.objects.extra(where=["name LIKE '%%" + name + "%%' COLLATE utf8_general_ci"])

But like this sql injection is possible.但是像这个 sql 注入是可能的。 We need to escape name variable.我们需要转义name变量。 I searched a lot for a function which just escapes a string for db.我搜索了很多只为 db 转义字符串的函数。 Found one in MySQL-python package but it can't escape unicode strings.MySQL-python包中找到了一个,但它无法转义 unicode 字符串。 Also package has literal method in connection but to use it we need an instance (maybe it is for db characteristic).包也有literal方法connection但要使用它,我们需要一个实例(也许它是为了 db 特性)。

At last I used Django's db.connection.cursor .最后我使用了 Django 的db.connection.cursor

from django.db import connection
cursor = connection.cursor()
name = cursor.db.connection.literal(name)[1:-1]  # [1:-1] excluding quotes

With this way we also need an instance but I suppose this not require a db connection.通过这种方式,我们还需要一个实例,但我想这不需要数据库连接。 And I suppose this method db independent.而且我想这种方法与数据库无关。 If I am wrong please correct me.如果我错了,请纠正我。

This above solution works.上述解决方案有效。 In case of getting the reverse order the following snippet如果获得相反的顺序,请使用以下代码段

sort_value = sort.strip()
if sort_value in ['name', '-name']:
    sort = Func('name', function='C', template='(%(expressions)s) COLLATE "%(function)s"')
if sort_value in ['-name']:
    f_res = queryset.order_by(sort).reverse()
else:
    f_res = queryset.order_by(sort)
return f_res

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM