Пользовательский поиск Django, rhs получает только %s
Я хочу создать пользовательский поиск icontainsall
таким образом, что когда пользователь ищет фразу с пробелом в ней, я хочу преобразовать ее в подобие всех слов с помощью AND (так что порядок слов не имеет значения). Т.е. если пользователь ищет 'egg milk'
, предикат становится name LIKE %egg% AND name LIKE %milk%
.
Вот как выглядит мой поиск:
from django.db.models import CharField, Lookup, TextField
class CaseInsensitiveContainsAllLookup(Lookup):
lookup_name = 'icontainsall'
def as_sql(self, compiler, connection):
lhs, lhs_params = self.process_lhs(compiler, connection)
rhs, rhs_params = self.process_rhs(compiler, connection)
print(lhs, rhs, lhs_params, rhs_params)
# shows "mytable"."name" %s [] ['egg milk']
predicate, params = [], []
# escape \ and %
rhs_list = rhs.replace('\\', '\\\\').replace('%', '\\%').split(' ')
for rhs in rhs_list:
params += lhs_params + rhs_params
predicate.append(f"LOWER({lhs}) LIKE LOWER({rhs})")
return ' AND '.join(predicate), params
CharField.register_lookup(CaseInsensitiveContainsAllLookup)
TextField.register_lookup(CaseInsensitiveContainsAllLookup)
Однако rhs
получает только %s
, но не egg milk
. Как мне добиться желаемого с помощью поиска?
Edit: Я отобразил lhs_params и rhs_params. Пытаюсь понять, почему это список. Если и когда я пойму, думаю, я смогу решить свою проблему.
Методы process_lhs
и process_rhs
возвращают кортеж, первым элементом которого является строка SQL-выражения, а вторым - список параметров SQL-выражения. Рассматривать rhs
как %s
имеет смысл, поскольку это реализация Lookup.process_rhs
по умолчанию. Нужное вам значение присутствует в rhs_params
.
Кроме того, вместо того, чтобы пытаться самостоятельно реализовать логику сравнения, не зависящую от регистра, вам следует позволить Django сделать тяжелую работу, используя IContains
встроенный поиск:
from django.db.models.lookups import IContains
class CaseInsensitiveContainsAllLookup(Lookup):
lookup_name = 'icontainsall'
def as_sql(self, compiler, connection):
lhs, lhs_params = self.process_lhs(compiler, connection)
rhs, rhs_params = self.process_rhs(compiler, connection)
flattened_rhs_params = []
for param in rhs_params:
flattened_rhs_params += param.split(' ')
predicate, params = [], []
for rhs_param in flattened_rhs_params:
contains_sql, contains_params = IContains(self.lhs, rhs_param).as_sql(compiler, connection)
params += contains_params
predicate.append(contains_sql)
return ' AND '.join(predicate), params
Примечание: если в вашем вводе есть несколько пробелов между словами, то при текущей реализации разбиения вы будете иметь пустые строки в flattened_rhs_params
, в зависимости от вашего случая использования вы можете захотеть отфильтровать эти пустые строки.