Michael
Michael

Reputation: 4461

Filter with arbitrary number or Q objects combined by OR logical operator

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?

Upvotes: 1

Views: 555

Answers (3)

Román M
Román M

Reputation: 21

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

Upvotes: 1

Sergey Gornostaev
Sergey Gornostaev

Reputation: 7787

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

Upvotes: 3

Todor
Todor

Reputation: 16020

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

Upvotes: 1

Related Questions