Вероятно, нам лучше создать вспомогательную функцию, которая создает объект Q
, который является дизъюнкцией нескольких элементов, например:
from django.db.models import Q
from functools import reduce
from operator import or_
def q_or_otherwise_true(iterable, *keys):
iterable = list(iterable)
if iterable:
return reduce(or_, [Q(**{key: val}) for val in iterable for key in keys])
else:
return Q()
Таким образом, генерируется Q
объектов, таких как:
>>> q_or_otherwise_true(['foo'], 'col1__icontains', 'col2__icontains')
<Q: (OR: ('col1__icontains', 'foo'), ('col2__icontains', 'foo'))>
>>> q_or_otherwise_true(['foo', 'bar'], 'col1__icontains', 'col2__icontains')
<Q: (OR: ('col1__icontains', 'foo'), ('col2__icontains', 'foo'), ('col1__icontains', 'bar'), ('col2__icontains', 'bar'))>
>>> q_or_otherwise_true([], 'col1__icontains', 'col2__icontains')
<Q: (AND: )>
тогда мы можем сгенерировать это следующим образом:
def get_queryset(self):
city = self.request.GET.get('city_name') or ''
user = self.request.GET.get('user_name') or ''
userqueries = user.split()
cityqueries = city.split()
return self.model.objects.filter(
q_or_otherwise_true(userqueries, 'first_name__icontains', 'last_name__icontains'),
q_or_otherwise_true(cityqueries, 'city__name__icontains'),
)
Это работает, потому что или q_or_otherwise_true
создает дизъюнкцию элементов, поскольку iterable
содержит какие-либо элементы.Если нет, он создает объект Q()
, который - при вызове .filter(..)
- ничего не отфильтровывает.Таким образом, это означает, что мы можем таким образом сделать соединение этих двух.
Функция может быть легко расширена для большего количества вызовов, просто делая дополнительный вызов q_or_otherwise_true
.