Django tips: template loading and rendering

Published November 1, 2007. Filed under: Django.

I’ve been reminded today by Maura that November is National Blog Posting Month, when — in theory — bloggers the world over try to keep up a pace of one entry every day. I don’t know how well this is going to go, but I’d like to give it a try. And, inspired by Drew McLellan’s excellent 24 ways “advent calendars” of web-design tips, I’m going to give it a theme: one Django tip every day during the month of November. Kicking off the series, I’d like to focus today on the deceptively-simple task of template loading and rendering.

The long way

If you’ve ever read through the Django tutorial, you know that manually loading and rendering a template is a bit of a chore, because it involves multiple separate steps. The canonical example looks like this:

from django.template import loader, Context

t = loader.get_template('my_template.html')
c = Context({ 'object_list': SomeModel.objects.all() })
rendered = t.render(c)

This shows off all of the steps involved:

  1. A template loader is used to locate the actual template to use and parses it into a Template object suitale for rendering.
  2. A Context of some sort is instantiated to provide the Template some variables to work with.
  3. The Template is rendered (via its render() method) using the Context.

Multiple templates

Before moving on to shortcuts, let’s stop for a second and consider a common use case: customizing templates on a per-object basis. For example, at work we have news stories organized into “sections” according to their topics; we have sections for things like local news, sports, politics, etc. Naturally, some of these sections need to get special treatment by having their own templates customized for a particular section’s topical theme.

Now, one way to do this would be to add an optional field on the Section model for specifying a custom template name, and use that if it’s specified or fall back to a default template if not. The view code might look like this (assuming we look up sections from a slug in the URL):

section = get_object_or_404(Section, slug__exact=slug)
if section.custom_template_name:
    t = loader.get_template(section.custom_template_name)
else:
    t = loader.get_template("news/section.html")

That’s awfully wordy, though, and it requires lots of editing of the Section objects. A better option — in most cases (sometimes, for example in the case of the FlatPage model in django.contrib.flatpages, it’s more convenient to use a field for this) — would be some sort of automatic method of checking for a custom template. For example, if there’s a section with the slug “sports”, it’d be nice to check for the template “news/section_sports.html” and then fall back, because then the only work to be done is creating that template. And we could adapt the above code to do it:

section = get_object_or_404(Section, slug__exact=slug)
try:
    t = loader.get_template("news/section_%s.html" % section.slug)
except TemplateDoesNotExist:
    t = loader.get_template("news/section.html")

This is still pretty nasty, though, and it’d get even worse if we wanted multiple possible levels of template customization. Fortunately, Django provides an easy solution: the select_template() method of the template loader, which takes a list of template names and returns the first one that actually exists. Here’s how it would work in our example:

section = get_object_or_404(Section, slug__exact=slug)
t = loader.select_template(["news/section_%s.html" % section.slug,
                            "news/section.html"])

Under the hood, select_template() simply runs through the list of template names in order, catching TemplateDoesNotExist when needed and returning the first template that loads successfully. This makes for a very compact and easy-to-use idiom for per-object template customization (if you’ve ever wondered how the Django admin lets you override templates on a per-model or per-application basis: it’s calling select_template() with a list of decreasingly-specific names).

Getting a response

Of course, that’s only half the battle; when you’re working in a view, you also need to return an HttpResponse, usually by doing something like (returning to the first example above, where we loaded a template in the variable t and created a Context named c):

return HttpResponse(t.render(c))

And that makes the whole thing even more tedious, which is why the documentation goes to great pains to point out the render_to_response() shortcut. Using render_to_response(), everything gets much simpler:

return render_to_response('my_template.html',
                        { 'object_list': SomeModel.objects.all() })

This collapses the whole process of loading a template, creating a Context, rendering the template and creating an HttpResponse into one easy step. I’d hope that anyone who spends more than an hour or two playing with Django picks up on this, but it’s worth pointing out in case you’ve never run across it.

What if you don’t want a response?

Of course, there are times when you don’t want to return an HttpResponse (or aren’t quite ready to do that yet). One example I run across all the time is using templates to programmatically generate email messages; passing some variables in and rendering a template makes for an extremely easy way to do this, but render_to_response doesn’t help at all here, since you want to feed that into the email-sending machinery, not the HTTP request/response handlers. That’s why there’s another (and, from my experience, less-well-known) function which stops short of creating an HttpResponse: django.template.loader.render_to_string(). Here’s an example:

from django.template.loader import render_to_string
email_body = render_to_string("my_email_template.txt", { 'name': 'Bob',
                                                        'message': 'Hello!' })

The argument signature here is pretty much identical, and in fact render_to_response() is really just a light wrapper around render_to_string() which stuffs the result into an HttpResponse before returning (render_to_response() does take a few extra optional arguments related to HTTP, though). I use this all the time for generating emails and other things which won’t necessarily end up as part of an HttpResponse (though it’s worth noting that if you use this for the subject of an email, you should take care to forcibly collapse it down to a single line; remember, email is sensitive to newlines).

Using RequestContext

Another common problem in template rendering is setting up some list of variables that need to appear in every (or nearly every) Context you use for template rendering: you’ll often want to have the current logged-in User available, the value of the MEDIA_URL setting, and sometimes project- or application-specific values.

The intuitive solution, from a Python programming point of view, would be to note that Context is a class, and so could easily be subclassed with the subclass set up to automatically include certain values without needing to explicitly list them every single time. And Django provides a built-in subclass of Context, called RequestContext, which does just that: in addition to a dictionary of variables specific to the view you’re writing, a RequestContext takes an HttpRequest as an argument and calls a set of functions called “context processors” to let them specify additional variables based on the request.

Although it’s covered in the Django documentation and has been written about in various blog posts (including one by yours truly), I’m always surprised at how many people don’t know about RequestContext, especially because it’s so easy to use. Returning to the original example we started with, here’s how it’d be rewritten to use RequestContext:

from django.template import loader, RequestContext

t = loader.get_template('my_template.html')
c = RequestContext(request, { 'object_list': SomeModel.objects.all() })
rendered = t.render(c)

Any context processors specified in your TEMPLATE_CONTEXT_PROCESSORS setting would be applied automatically; if you’re using the default set, that’d get things like the current User, the value of MEDIA_URL, the currently-active translation (for the internationalization system), etc.

Putting it all together

And the two template-rendering shortcuts — render_to_response() and render_to_string() — both allow you to take advantage of the extra features we’ve covered here. For example, they both allow you to pass a list of template names, and (by checking whether the argument was a list or a string) will automatically use select_template() instead of get_template(). This means that for the example above of customizable per-section templates, we could do:

section = get_object_or_404(Section, slug__exact=slug)
return render_to_response(["news/section_%s.html" % section.slug,
                           "news/section.html"],
                          { 'section': section })

Both functions also support an optional context_instance argument which lets you drop in any Context — or, more importantly, any subclass of Context — you like. For example:

return render_to_response('my_template.html',
                         { 'object_list': SomeModel.objects.all() },
                         context_instance=RequestContext(request))

Taken together, this means you’ve got access to simple, easy-to-remember shortcuts for quickly rendering to a string or HttpResponse, using any type of Context you like, and with the full flexibility of select_template(). Most of the time, you can get all of that into a single line of code (if you don’t mind it running a bit long to accommodate the arguments). And that’s nothing to sneeze at.