Filter with arbitrary number or Q objects combined

2019-09-16 15:09发布

Django 1.10.1

Search form. The user inserts words separated by spaces. Necessary to find objects with ANY of these words in the title field.

I'm planning to use something like this:

Article.objects.filter(
    Q(title__icontains="table") | Q(title__icontains="helm")
)

I can make Q objects easily: q = Q(title__icontains="table").

But the obstacle is how to pass arguments to the filter method.

https://docs.djangoproject.com/en/1.10/topics/db/queries/

The syntax is filter(**kwargs).

With a loop I can prepare a dictionary like this :

q_objects = {"1": Q(title__icontains="table"), "2": Q(title__icontains="glasses")}

But the problem is with that "|".
How to pass it to the filter method is a mystery to me. In other words I fail to build a filter with OR logical operator.

Could you help me?

3条回答
够拽才男人
2楼-- · 2019-09-16 15:33
query = Q()
for word in words:
    query |= Q(title__icontains=word)
Article.objects.filter(query)

or

from operator import __or__ as OR
from functools import reduce

query = [Q(title__icontains=word) for word in words]
Article.objects.filter(reduce(OR, query))
查看更多
Rolldiameter
3楼-- · 2019-09-16 15:35

You can do something like this:

queryset = MyModel.objects.all()
queries = ['table, helm']
filter_ = 'title__icontains'

queryset.filter(reduce(lambda q1,q2: q1|q2, [Q(**{filter_: q}) for q in queries], Q()))

#This will build a QuerySet similar to this one:
queryset.filter(Q(title__icontains='table')|Q(title__icontains='helm')) 
查看更多
等我变得足够好
4楼-- · 2019-09-16 15:40

In the same line as proposed by @Todor and based on this post, I like this syntax better:

reduce(operator.or_, (Q(title__icontains=x) for x in ['x', 'y', 'z']))

The full code with your example could be:

list_of_words = ['table', 'helm']   # words wanted by the user

Article.objects.filter(
     reduce(operator.or_, (Q(title__icontains=word) for word in list_of_words))
)
查看更多
登录 后发表回答