Skip to content
Q and operator.or_

Q and operator.or_

April 9, 2009

I’ve finally settled on a nice syntax for OR-ing Django Q objects.

For a simple site search feature I needed to search for a term across several fields in a model. Suppose the model looks like this:

class BlogPost(models.Model):
    title = models.CharField(max_length=100)
    body = models.TextField()
    summary = models.TextField()

And you have a view method that accepts a parameter q for searching across the title, body and summary fields. I want to find objects that contain the q phrase in any of those fields. I need to build a QuerySet with a filter that is the equivalent of

queryset = BlogPost.objects.filter(
    Q(title__icontains=q) | Q(body__icontains=q) | Q(summary__icontains=q)
)

That’s not too much of a hassle for this simple example, but in cases where the fields you are searching are chosen dynamically, or where you just have an awful lot of fields to search against, I think it is nicer to do it like so:

import operator

search_fields = ('title', 'body', 'summary')
q_objects = [Q(**{field + '__icontains':q}) for field in search_fields]
queryset = BlogPost.objects.filter(reduce(operator.or_, q_objects))

Nice one! The list comprehension gives me a list of Q objects generated from the names in search_fields, so it is easy to change the fields to be searched. And using reduce and operator.or_ gives me the required OR filter in one line.

I see for Python 3 reduce has been moved to the functools module.

This stuff never used to be that obvious to me. It kind of isn’t even now.

P.S. I promise I am not writing a blog engine at this time, it was just for the example.

Last updated on