Django reverse to содержит/значки

В этом вопрос был решен проблемой для операции обратного LIKE в SQL, например, если именем поля является "Peter Johnson", мы могли бы найти его по такому запросу

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

Есть ли способ сделать такую ​​вещь в объекте Django Q (я строю большой запрос, поэтому использование необработанного SQL-запроса не будет рациональным)?

Ответ 1

К сожалению, 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, "%"), но это сделает его специфичным для СУБД, что не является хорошо).

Реверсивный LIKE сам должен, вероятно, работать с любыми крупными СУБД, но я тестировал его только на sqlite и postgres.

Возможно, кто-то должен обобщить мое решение и создать многозадачное приложение DBMS-agnostic со специальным подклассом queryset/manager/Q-object для этой конкретной задачи...

Ответ 2

Если вы используете последнюю версию Django (1.10 или новее) и используете Postgres, ORM может справиться с этим. Проверьте документы.

A trigram_similar lookup доставит вам то, что вы ищете:

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

Не забудьте включить этот поиск, включив расширение pg_tgrm. Вы можете сделать это с помощью миграции django.

И вам нужно добавить 'django.contrib.postgres' в настройку INSTALLED_APPS.

Ответ 3

В то время как дополнения предоставляют расширенную сложную функциональность для крайних случаев, дополнения должны рассматриваться как последнее средство и, вероятно, в какой-то момент будут устаревшими.

Это может быть достигнуто с помощью аннотирования и фильтрации.

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))

Ответ 4

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