, Johann Schmitz

If you use Foreign Keys on a model in Django, you might not be aware of performance issues until it hits you. Navigating through ForeignKey relationships in your code/templates are very easy, but creates a db query every time.

Lets look at this problem with a simple model:

class Category(models.Model):
    name = models.CharField(max_length=255)

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

    category = models.ForeignKey(Category)

    created_by = models.ForeignKey(User, related_name='+')
    modified_by = models.ForeignKey(User, related_name='+', blank=True, null=True)

For a list of articles you could write something like this:

for article in Article.objects.all(): 
    print "%s by %s in %s" % (article.title, article.modified_by or article.created_by, article.category.name)

Run the code and look at the generated queries (either in the django development server output or, a litte bit prettier, in the SQL tab of the Django Debug Toolbar. With 3 Article objects, this will create 7 SQL queries to your database:

  • One for the list of article objects
  • One for the modified_by field on each object
  • One for the created_by field on each object
  • One for the category field on each object

To work around this issue, you can call select_related() on the Manager object of the Article class. This will combine all referenced objects into one query, which is usually a lot faster! Have a look into the documentation for a list of parameters.