Отфильтровать базу данных Django для поля, содержащего любое значение в массиве
У меня есть модель django и поле, представляющее полное имя пользователя. Мой клиент хочет, чтобы я настроил фильтр для поиска пользователя на основе массива строк, где все они должны быть нечувствительны к регистру, содержащиеся в полном имени.
Например
Если пользователи full_name = "Keith, Thomson S."
И у меня есть список ['keith','s','thomson']
Я хочу выполнить эквивалент фильтра
Profile.objects.filter(full_name__icontains='keith',full_name__icontains='s',full_name__icontains='thomson')
Проблема в том, что этот список может иметь динамический размер, поэтому я не знаю, как это сделать.
У кого-нибудь есть идеи?
Ответы
Ответ 1
Сделайте последовательные вызовы filter
, например:
queryset = Profile.objects.all()
strings = ['keith', 's', 'thompson']
for string in strings:
queryset = queryset.filter(full_name__icontains=string)
В качестве альтернативы вы можете &
собрать кучу объектов Q
:
condition = Q(full_name__icontains=s[0])
for string in strings[1:]:
condition &= Q(full_name__icontains=string)
queryset = Profile.objects.filter(condition)
Более загадочный способ писать это, избегая явного цикла:
import operator
# ...
condition = reduce(operator.and_, [Q(full_name__icontains=s) for s in strings])
queryset = Profile.objects.filter(condition)
Ответ 2
Еще меньше, используя функции operator
and_
или or_
, чтобы объединить список условий Q()
from operator import and_, or_
li = ['keith', 's', 'thompson']
Элементы, соответствующие всем строкам (and_
)
Profile.objects.filter(reduce(and_, [Q(full_name__icontains=q) for q in li]))
Элементы, которые соответствуют любой из строк (or_
)
Profile.objects.filter(reduce(or_, [Q(full_name__icontains=q) for q in li]))
Функция Q()
реализует __or__()
и __and__()
для объединения двух объектов Q()
, поэтому их можно вызвать с помощью соответствующих функций operator
.
Ответ 3
что-то в этом роде:
array = ['keith', 's', 'thomson']
regex = '^.*(%s).*$' % '|'.join(array)
Profile.objects.filter(full_name__iregex=regex)
EDIT: это неправильно, OP хочет имена, которые содержат все строки одновременно.