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