在 Django 中应用多属性搜索



我正在尝试在我的列表视图中实现多个属性的搜索。 我不想为每个属性使用多个 if-else。

这是我当前在列表视图中搜索的代码:

def get_queryset(self):
city = self.request.GET.get('city_name') or ''
user = self.request.GET.get('user_name') or ''
if (city != '' or user!=''):
userqueries = user.split() 
cityqueries = city.split() 
if len(userqueries) and len(cityqueries):
qset1 =  functools.reduce(operator.__or__, [
Q(first_name__icontains=query) | Q(last_name__icontains=query) for query in userqueries])
qset2 =  functools.reduce(operator.__or__, [Q(city__name__icontains=query)  for query in cityqueries])
object_list = self.model.objects.filter(qset1 , qset2)
elif len(userqueries):
qset1 =  functools.reduce(operator.__or__, [
Q(first_name__icontains=query) | Q(last_name__icontains=query) for query in userqueries])
object_list = self.model.objects.filter(qset1)
elif len(cityqueries):
qset1 =  functools.reduce(operator.__or__, [Q(city__name__icontains=query)  for query in cityqueries])
object_list = self.model.objects.filter(qset1)
else:
object_list = self.model.objects.all()
return object_list

如果我添加一个属性:

city = self.request.GET.get('city_name') or ''
user = self.request.GET.get('user_name') or ''
state = self.request.GET.get('state_name') or ''
if (city != '' or user!='' or state!=''):
userqueries = user.split() 
cityqueries = city.split() 
statequeries = state.split() 
if len(userqueries) and len(cityqueries):
qset1 =  functools.reduce(operator.__or__, [
Q(first_name__icontains=query) | Q(last_name__icontains=query) for query in userqueries])
qset2 =  functools.reduce(operator.__or__, [Q(city__name__icontains=query)  for query in cityqueries])
object_list = self.model.objects.filter(qset1 , qset2)
elif len(userqueries) and len(statequeries):
qset1 =  functools.reduce(operator.__or__, [
Q(first_name__icontains=query) | Q(last_name__icontains=query) for query in userqueries])
qset2 =  functools.reduce(operator.__or__, [Q(city__state__name__icontains=query)  for query in statequeries])
object_list = self.model.objects.filter(qset1 , qset2)
elif len(userqueries):
qset1 =  functools.reduce(operator.__or__, [
Q(first_name__icontains=query) | Q(last_name__icontains=query) for query in userqueries])
object_list = self.model.objects.filter(qset1)
elif len(cityqueries):
qset1 =  functools.reduce(operator.__or__, [Q(city__name__icontains=query)  for query in cityqueries])
object_list = self.model.objects.filter(qset1)
elif len(statequeries):
qset1 =  functools.reduce(operator.__or__, [Q(city__state__name__icontains=query)  for query in statequeries])
object_list = self.model.objects.filter(qset1)

我想将所有这些条件合并为一个:

if len(userqueries) and len(cityqueries):
elif len(userqueries):
elif len(cityqueries):

我们可能最好做一个帮助函数来构造一个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'),
)

这是有效的,因为 orq_or_otherwise_true在给定iterable包含任何元素的情况下,元素的分离。如果不是,它会构造一个Q()对象,该对象 - 在.filter(..)调用中 - 不会过滤掉任何内容。因此,这意味着我们可以将这两者进行共轭。

只需进行额外的q_or_otherwise_true调用,即可轻松将该函数扩展到更多调用。

最新更新