About •  Books •  Apps •  Jobs •   Feeds •  Twitter
Login

The power of Q

Q objects are a great tool in Django

No not that Q

Q objects are lumbered with a name that's very hard to Google. Down there with F objects and Plone's event. You'll probably come across Q objects when you need to complicated lookups such as not, or and so on. But these objects are so useful.

Complex lookups

You can make Q objects for the lookups you'd like. For example:

>>> from django.db.models import Q
>>> from listener.models.error import Error # our example model

To find all objects that archived and have a priority of 1, we could do:

>>> Error.objects.filter(archived=False).filter(priority=1).count()
39592

The equivalent in Q objects is:

>>> Error.objects.filter(Q(archived=False) & Q(priority=1)).count()
39592

But now we can at least do an or quite easily (archived or have a priority of 1):

>>> Error.objects.filter(Q(archived=False) | Q(priority=1)).count()
195871

And how about archived and a priority of not 1:

>>> Error.objects.filter(Q(archived=False) & ~Q(priority=1)).count()
41545

List of lookups

One thing I do quite often is build up the query dynamically. You can do that easily in Python by making a dictionary and passing that to filter. You can also do it by building up a list of Q objects. For example:

>>> qs = [Q(archived=False),]
>>> qs.append(Q(priority=1))
>>> qs
[<django.db.models.query_utils.Q object at 0x17a0d90>, <django.db.models.query_utils.Q object at 0x17a0d50>]

We've now got a list of Q objects and we can do a filter. The use of reduce and operator.or_, applies the | to all the elements in the list:

>>> import operator
>>> Error.objects.filter(reduce(operator.or_, qs)).count()
195871

Similarly for an and:

>>> Error.objects.filter(reduce(operator.and_, qs)).count()
39592

Pickling lookups

If you would like to store your lookups you can pickle the list of objects. This means you can save the Q objects and use them again later. For example:

>>> import pickle
>>> saved = pickle.dumps(qs)
>>> Error.objects.filter(reduce(operator.and_, pickle.loads(saved))).count()
39592

Pickling is a useful way of saving the queries for use again later. In some situations it can be benefical to pickle the queries as opposed to pickling the results. Django documentation notes that "If you pickle a QuerySet, this will force all the results to be loaded into memory prior to pickling." (docs).

Comments

There are 5 comment(s).

Heh, I think this is the first article I see where someone actually gets the benefits of Q objects - usually they were lambasted on the list back then as "ugly". But yes, storing Q objects is one neat thing for example if you want to have saved searches for example. And dynamic construction of queries was one of the reasons to actually invent them (I was the guy responsible for Q objects in Django ;) )

http://simon.rfc1437.de/id/ on Jan, 09

I use django.db API because I have to. Other tools (Storm by Canonical or Axiom by Divmod) expose a bit better API, IMO.

https://www.google.com/accounts/o8/id?id=AItOawnQOcR0gw9bOqtxrznonnFFGOdNAK9ipRs on Jan, 13

Well thanks for Q objects, they work great! Just that damn name :)

Andy McKay on Jan, 13

Hi - great posting, it's helped me get started with using Q objects. I'm wondering if you have any experience with the question I've posted at: http://stackoverflow.com/questions/2431276/reverse-search-best-practices Best

https://www.google.com/accounts/o8/id?id=AItOawl9aTV5IhKUEdHBF9h8uGWSLhVV0-amd_M on Mar, 12

hey thanks for this post, really useful

on Jul, 20

Login to add comments