Django反向包含/ icontains

时间:2013-03-17 20:30:36

标签: django django-models django-queryset

this问题解决了SQL中反向LIKE操作的问题,例如,如果字段名称是“Peter Johnson”,我们可以通过这样的查询找到它:

select name from user where "Mr. Peter Johnson" like CONCAT('%', name, '%')

有没有办法在Django Q对象中执行此类操作(我正在构建一个大查询,因此使用原始SQL查询将不合理)?

4 个答案:

答案 0 :(得分:3)

不幸的是,Django的ORM没有任何内置的反向LIKE。但是.extra()子句可能比原始查询更容易。

我使用过这样的东西:

qs.extra(
    where=['''%s LIKE %s.%s'''],
    params=(
        "string to match",
        FooModel._meta.db_table,
        "bar_field",
    ),
)

上面代码的问题是

1)它不能以这种形式使用sqlite后端(“语法错误附近。”,它确实与查询中硬编码的表/列名一起使用...这并不总是安全且总是很难看);

和2)它要求FooModel.bar_field拥有数据%in like style%,因此您无法匹配任意字符串(这可以通过%s LIKE CONCAT("%", %s.%s, "%")等查询修复,但它会使其成为特定于DBMS的,这不好)。

反转LIKE本身可能适用于任何主要的DBMS,但我只在sqlite和postgres上测试过。

也许有人应该推广我的解决方案并创建一个可重复使用的,与DBMS无关的应用程序,其中包含用于此特定任务的特殊子类查询集/管理器/ Q对象...

答案 1 :(得分:2)

如果您使用的是最新版本的Django(1.10或更高版本)并使用Postgres,则ORM可以处理此问题。 Check out the docs.

trigram_similar查询将为您提供所需内容:

qs = MyModel.objects.filter(name__trigram_similar='Mr. Peter Johnson')

不要忘记启用pg_tgrm扩展名来启用此查找。您可以使用django migration

执行此操作

您需要将'django.contrib.postgres'添加到INSTALLED_APPS设置中。

答案 2 :(得分:0)

尽管Extras确实为边缘情况提供了扩展的复杂功能,但Extras应该被视为万不得已,并且在某些时候可能会被弃用。

这可以通过注释和过滤来实现。

from django.db.models import F, Value, CharField

MyUserModel.objects \
     .annotate(my_name_field=Value('Mr. Peter Johnson', output_field=CharField())) \
     .filter(my_name_field__icontains=F('name'))

通用:

from django.db.models import F, Value, CharField

@staticmethod
def reverse_case_insensitive_contains(model, search_field_name: str, search_field_value: str):
    return model.objects \
        .annotate(search_field=Value(search_field_value, output_field=CharField())) \
        .filter(search_field__icontains=F(search_field_name))

答案 3 :(得分:-2)

output = MyModel.objects.filter(Q(name__contains="Mr. Peter Johnson"))