使用可变数量的参数过滤多个 Django 模型字段
Filter multiple Django model fields with variable number of arguments
我正在通过匹配多个 table 和这些 table 中的多个字段来查找记录的选项来实现搜索功能。
假设我想通过 his/her 名字或姓氏,或通过存储在与 Customer
不同模型中的已放置 Order
的 ID 来查找 Customer
。
我已经实现的简单场景是用户只在搜索字段中键入单个单词,然后我使用 Django Q
使用直接字段引用或 related_query_name
引用来查询 Order
模型,例如:
result = Order.objects.filter(
Q(customer__first_name__icontains=user_input)
|Q(customer__last_name__icontains=user_input)
|Q(order_id__icontains=user_input)
).distinct()
小菜一碟,完全没有问题。
但是如果用户想要缩小搜索范围并在搜索字段中键入多个词怎么办。
示例:用户输入 Bruce
并作为搜索结果返回了大量记录。
现在 he/she 想要更具体并将客户的姓氏添加到 search.So 搜索变成 Bruce Wayne
,在将其拆分成单独的部分后我有 Bruce
和 Wayne
。显然,我不想搜索 Orders
模型,因为 order_id
是一个单词实例,它足以立即找到客户,所以对于这种情况,我将其完全排除在查询之外。
现在我正在尝试通过名字和姓氏来匹配客户,我还想处理提供的数据顺序随机的情况,以正确处理 Bruce Wayne
和 Wayne Bruce
,这意味着我仍然有客户的全名,但名字和姓氏的位置不固定。
这就是我正在寻找答案的问题:如何构建查询来搜索模型的多个字段而不知道哪个搜索词属于哪个 table。
我猜这个解决方案很简单,而且肯定有一种优雅的方法来创建这样一个动态查询,但我想不出一个方法。
您可以动态地将可变数量的 Q 对象组合在一起以实现您想要的搜索。下面的方法可以轻松添加或删除要包含在搜索中的字段。
from functools import reduce
from operator import or_
fields = (
'customer__first_name__icontains',
'customer__last_name__icontains',
'order_id__icontains'
)
parts = []
terms = ["Bruce", "Wayne"] # produce this from your search input field
for term in terms:
for field in fields:
parts.append(Q(**{field: term}))
query = reduce(or_, parts)
result = Order.objects.filter(query).distinct()
reduce
的使用通过 OR 将 Q 对象组合在一起。感谢那部分答案 goes to this answer.
我想出的解决方案相当复杂,但它的工作方式完全符合我想要处理这个问题的方式:
search_keys = user_input.split()
if len(search_keys) > 1:
first_name_set = set()
last_name_set = set()
for key in search_keys:
first_name_set.add(Q(customer__first_name__icontains=key))
last_name_set.add(Q(customer__last_name__icontains=key))
query = reduce(and_, [reduce(or_, first_name_set), reduce(or_, last_name_set)])
else:
search_fields = [
Q(customer__first_name__icontains=user_input),
Q(customer__last_name__icontains=user_input),
Q(order_id__icontains=user_input),
]
query = reduce(or_, search_fields)
result = Order.objects.filter(query).distinct()
我正在通过匹配多个 table 和这些 table 中的多个字段来查找记录的选项来实现搜索功能。
假设我想通过 his/her 名字或姓氏,或通过存储在与 Customer
不同模型中的已放置 Order
的 ID 来查找 Customer
。
我已经实现的简单场景是用户只在搜索字段中键入单个单词,然后我使用 Django Q
使用直接字段引用或 related_query_name
引用来查询 Order
模型,例如:
result = Order.objects.filter(
Q(customer__first_name__icontains=user_input)
|Q(customer__last_name__icontains=user_input)
|Q(order_id__icontains=user_input)
).distinct()
小菜一碟,完全没有问题。
但是如果用户想要缩小搜索范围并在搜索字段中键入多个词怎么办。
示例:用户输入 Bruce
并作为搜索结果返回了大量记录。
现在 he/she 想要更具体并将客户的姓氏添加到 search.So 搜索变成 Bruce Wayne
,在将其拆分成单独的部分后我有 Bruce
和 Wayne
。显然,我不想搜索 Orders
模型,因为 order_id
是一个单词实例,它足以立即找到客户,所以对于这种情况,我将其完全排除在查询之外。
现在我正在尝试通过名字和姓氏来匹配客户,我还想处理提供的数据顺序随机的情况,以正确处理 Bruce Wayne
和 Wayne Bruce
,这意味着我仍然有客户的全名,但名字和姓氏的位置不固定。
这就是我正在寻找答案的问题:如何构建查询来搜索模型的多个字段而不知道哪个搜索词属于哪个 table。
我猜这个解决方案很简单,而且肯定有一种优雅的方法来创建这样一个动态查询,但我想不出一个方法。
您可以动态地将可变数量的 Q 对象组合在一起以实现您想要的搜索。下面的方法可以轻松添加或删除要包含在搜索中的字段。
from functools import reduce
from operator import or_
fields = (
'customer__first_name__icontains',
'customer__last_name__icontains',
'order_id__icontains'
)
parts = []
terms = ["Bruce", "Wayne"] # produce this from your search input field
for term in terms:
for field in fields:
parts.append(Q(**{field: term}))
query = reduce(or_, parts)
result = Order.objects.filter(query).distinct()
reduce
的使用通过 OR 将 Q 对象组合在一起。感谢那部分答案 goes to this answer.
我想出的解决方案相当复杂,但它的工作方式完全符合我想要处理这个问题的方式:
search_keys = user_input.split()
if len(search_keys) > 1:
first_name_set = set()
last_name_set = set()
for key in search_keys:
first_name_set.add(Q(customer__first_name__icontains=key))
last_name_set.add(Q(customer__last_name__icontains=key))
query = reduce(and_, [reduce(or_, first_name_set), reduce(or_, last_name_set)])
else:
search_fields = [
Q(customer__first_name__icontains=user_input),
Q(customer__last_name__icontains=user_input),
Q(order_id__icontains=user_input),
]
query = reduce(or_, search_fields)
result = Order.objects.filter(query).distinct()