Отфильтровать базу данных 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 хочет имена, которые содержат все строки одновременно.