使用可变数量的参数过滤多个Django模型字段

时间:2018-04-19 21:42:37

标签: django django-queryset django-q

我实现了搜索功能,可以选择通过匹配这些表中的多个表和多个字段来查找记录。

说我想通过他/她的名字或姓氏来找到Customer,或者通过存储在与Order不同的模型中的Customer ID来找到Q。 我已经实现的简单方案是用户只在搜索字段中输入单个单词,然后我使用Django Order使用直接字段引用或related_query_name引用来查询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() 模型,如:< / p>

Bruce

一块蛋糕,完全没问题。

但是,如果用户想要缩小搜索范围并将多个单词键入搜索字段,那该怎么办呢。

示例:用户输入了Bruce Wayne,并在搜索结果中收回了大量记录。

现在他/她希望更加具体,并添加客户的姓氏进行搜索。搜索变为Bruce,将此分成不同的部分后,我{{1} }和Wayne。显然,我不想搜索Orders模型,因为order_id是一个单字实例,而且它足以立即找到客户,所以对于这种情况,我放弃了它根本没有查询。

现在我尝试使用名字和姓氏来匹配客户,我还想处理提供数据的顺序是随机的情况,以正确处理Bruce WayneWayne Bruce ,意思是我仍然有客户的全名,但名字和姓氏的位置都没有修复。

这就是我正在寻找答案的问题:如何构建查询,搜索多个模型字段而不知道哪个搜索字属于哪个表。

我猜这个解决方案是微不足道的,确实是一种优雅的方式来创建这样一个动态查询,但我无法想办法。

2 个答案:

答案 0 :(得分:1)

您可以动态或将可变数量的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通过将Q对象组合在一起来组合它们。相信答案的那一部分goes to this answer

答案 1 :(得分:0)

我提出的解决方案相当复杂,但它的工作原理与我想要处理此问题的方式完全相同:

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()