Because of it’s origins in a busy newsroom, Django makes optimizing web application performance a breeze. Some high-level examples of this are the awesome cache framework and the shared nothing approach. But even with these tools it’s important to write code that performs as efficiently as possible – otherwise things like caching aren’t really optimizing, they’re simply minimizing performance loss due to bad code.
(“Make it more awesome” and “make it suck less” are two very different directives).
Minimizing database queries is a preemptive measure you can take to ensure your application is built to scale right from the start. Luckily, Django makes it very easy to bake this right into your code.
The extra() function makes it possible to attach object attributes which normally would require an additional database query. A simple example is probably the clearest way to illustrate this.
I’ll create two basic models, Author and Book, and a template which lists all authors and the number of books they’ve written. The first method uses the RelatedManager API Django automatically gives us, and the second method uses a custom Manager class which encapsulates the extra() function.
Method #1: Using RelatedManager.count()
So here are the super-simple models we’ll be working with:
And the template to list authors. Notice how it’s using the automatically available {{ author.book_set.count }} to show the number of books related to that author?
After adding some data and debug code, here’s what we get:

Query times are nil because I’m running this on my local database.
You can see there are 4 queries executing here. The first retrieves a list of all the the Author objects. The next three queries, which retrieve the number of related books, are the result of calling {{ author.book_set.count }} once per author.
The number of queries increases linearly with the number of authors, because every new author we list results in an additional query to get the book count. This means a list of 1000 authors will result in 1000 queries — yikes!
extra() to the rescue!
Method #2: Custom Manager with QuerySet.extra()
The following query will fetch all Author objects and use a subquery to add a book_count attribute to each object. The beauty in this approach is that Django’s ORM will translate this into a single SQL statement for us:
We can easily use this to create a custom AuthorManager to override get_query_set() and ensure book_count is always available to us:
Then update our template to use the book_count attribute:
And you can see it’s been reduced to a single query.

Now that’s kablamo. A quick and simple technique that can have a huge impact on your application’s performance.
Have your own little nuggets of performance-fu? Drop a comment. I’d love to know the tricks everyone else has up their sleeve.
1 Comment
Jon
May 06, 2010
Leave a comment