Brad Montgomery

Hi! I'm Brad Montgomery.

Thanks for visiting my site. I'm a software developer living in the suburbs of Memphis, TN. I'm a python programming language enthusiast and I'm quite fond of the Django Web Framework.

I also organize the Memphis Python user group. If you're ever in my neighborhood, and you'd like to meet other Pythonistas, please get in touch!

Want to know more? Check out my about page.

"Adding" Q objects in Django

Published by bkmontgomery on June 26, 2009, 4:47 a.m..

I've got a Django app with the following Model:


class Story(models.Model):
title = models.CharField(max_length=255)
content = models.TextField()


The Problem:
I wanted to build a simple search feature that OR'ed all the search terms. Essentially, I wanted SQL resembling the following:
SELECT * from myapp_stories where 
title LIKE '%term1%' OR content LIKE '%term1%' OR
title LIKE '%term2%' OR content LIKE '%term2%';


The Solution:
You can add django's Q objects together! This is a feature not currently discussed in the docs, but I dug through the source code and I discovered that a Q object is really just a node in a Tree! More specifically, Q is a subclass of django.utils.tree.Node (check it out, it's cool!) A Node has a attribute called a connector. Q objects have two possible connectors: AND and OR. But how do we connect Q objects? Well, a Node has a handy add(node, conn_type) method whose parameters include another Node and a connection type.

As previously mentioned, the possible connection types for Q objects are AND and OR, so Q objects can be added together by doing something like this:

# ANDing Q objects
q_object = Q()
q_object.add(Q(), Q.AND)

# ORing Q objects
q_object = Q()
q_object.add(Q(), Q.OR)


So, the solution to my Search view is as follows:


from django.db.models import Q
from models import Story

def search(request):
'''
Generic Search: GET should contain the following:
terms - the search keywords separated by spaces
'''
terms = request.GET.get('terms', None)
term_list = terms.split(' ')

stories = Story.objects.all()

q = Q(content__icontains=term_list[0]) | Q(title__icontains=term_list[0])
for term in term_list[1:]:
q.add((Q(content__icontains=term) | Q(title__icontains=term)), q.connector)

stories = stories.filter(q)

return render_to_response('myapp/search.html', locals(), \
context_instance=RequestContext(request))


Needless to say, Q objects are quite powerful!

Tagged with: django, python, web

comments powered by Disqus