Фильтровать базу данных Django по полю, содержащему любое значение в массиве - PullRequest
19 голосов
/ 21 января 2012

У меня есть модель django и поле, представляющее полное имя пользователя. Мой клиент хочет, чтобы я настроил фильтр для поиска пользователя на основе массива строк, где все они должны быть без учета регистра в полном имени.

Например

Если пользователи full_name = "Keith, Thomson S."

А у меня есть список ['keith','s','thomson']

Я хочу выполнить фильтр, эквивалентный

Profile.objects.filter(full_name__icontains='keith',full_name__icontains='s',full_name__icontains='thomson')

Проблема в том, что этот список может иметь динамический размер, поэтому я не знаю, как это сделать.

У кого-нибудь есть идеи?

Ответы [ 3 ]

41 голосов
/ 21 января 2012

Выполните последовательные звонки на 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)
8 голосов
/ 26 мая 2016

Еще короче, используя функции 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.

2 голосов
/ 21 января 2012

что-то вроде этих строк:


array = ['keith', 's', 'thomson']
regex = '^.*(%s).*$' % '|'.join(array)
Profile.objects.filter(full_name__iregex=regex)

РЕДАКТИРОВАТЬ: это неправильно, ОП хочет имена, которые содержат все строки одновременно.

...