Quick Django Trick

Recently while playing around with a Django model in the always awesome iPython shell I discoved a neat feature of the Django ORM.  It's basically a way to get the id of a related object without actually triggering a query to get all of the related object's data.

Frequently when working with a model which has a foreign key, I simply want to access the id of the related item and I don't care about any of the related item's other information.  Situations where this comes up include generating links and building queryset filters.  Unfortunately if I follow the normal Django style and do something like "item.related.id", the Django ORM will fire off an extra query to get all of the information of the related object (which I don't care about).  While this is far from tragic it is still unnecessary work since all I care about is the object's id and that is already contained in the "item" object.

Fortunately there is an alternative!  Instead of getting the id via "item.related.id" one can say "item.related_id".  Using this method, no extra query is performed and I get just the id value I was looking for.

There are two things to be aware of with this trick.  First I have not found this feature documented anywhere with a cursory search of the Django docs.  This means that I am not sure how much this feature is actually supported and how permament it may or may not be.  Second while I have not tested it, I suspect that if your foreign key has a custom database field name, the field on the model will match that custom field name.

As I come across any other quick Django tricks I may start making them a regular feature here on the blog.  If you found this intersting or have your own quick django trick, let me know and leave a comment!

Working around MySQL's horrible "ORDER BY rand()" in Django

Recently I've been working on a web 2.0ish community site written in Django. As is frequently the case with such sites I often need to create lists or collections of psuedo-randomly selected items. For example on a user's profile page there may be a box showing a few of the user's friends, another box with some of the user's
pictures, or a box with random comments the user has made.

Ideally in this type of situation I would simply preform a query with what ever rules I need and a random "order by" statement such as the following:
pictures = User.picture_set.filter(private=False).order_by('?')[:6]

Which generates SQL similar to:
SELECT * FROM `pictures`
WHERE private = 0 AND user_id = 5
ORDER BY rand()

In an ideal world this works great, I only get back as much data as I need and the site stays interesting because what the user sees is constantly changing/rotating. Unfortunately unless you are using PostgreSQL or have less than a few hundred rows in the pictures table you are not in an ideal world. Under both MySQL 4 and 5 random ordering is horrifyingly inefficient. Adding that "ORDER BY rand()" can make a query that took a few milliseconds to run instead take tens of seconds. The problem gets even worse when you are working with a table containing millions of rows and thousands of rows which meet the criteria of your query. What is one to do?

Installing Django on OS X 10.5 (Leopard)

This is a quick update to my previous installing django posts.

Today I got my hands on Leopard, so far I've been happy but went I went about reinstalling Django I hit a snag.  MacPort would encounter an error whenever it tried to build PostgreSQL.  If you can wait a little while the port maintainer for PostgreSQL should fix the problem.

On the other hand if you need Django running today you can follow the steps at Lee Packham's Corner to get PostgreSQL running.  Unfortunately this will get you PostgreSQL 8.2 (not inherently a bad thing) which means you will need to manually build and install psycopg2 since the MacPorts psycopg2 package depends on PostgreSQL 8.1.  Here's what you will need to do to manually build and install pyscopg2:

  • Download the source from http://www.initd.org/tracker/psycopg/wiki/PsycopgTwo.

  • Extract the files using the command tar -xzf psycopg2-latest.tar.gz.

  • Open the file setup.cfg file and look for the line that starts with pg_config=

  • Change the line to read pg_config=/opt/local/var/macports/software/postgresql82/8.2.5_0+darwin_9/opt/local/lib/postgresql82/bin/pg_config

  • Run the command python setup.py build

  • Run the command python setup.py install

Now you should be all set!

On a related note if your interested in learning about Djago and you'll be around Poughkeepsie, NY this Wednesday stop by at the MHVLUG meeting, I'll be giving a Intro to Django talk.