Posted Saturday, February 13, 2010 at 8:08 p.m. by Chris Amico in Projects about Django, Python, recipes and templates
One more quick code recipe before I jump back into the Journalism to Django series. In my last post, I mentioned that I set up permalinked paragraphs on a couple recent entries, using a different technique on each one. You might be wondering how I did that.
This is documented, but it took me a while before I realized how simple it is. The key is Django's get_template and select_template functions, which are part of the template system. Get template takes a string and gets a template. Select template chooses the first one that matches from a list or tuple.
Django's documentation suggests this pattern:
You can use
select_template()for super-flexible "templatability." For example, if you've written a news story and want some stories to have custom templates, use something likeselect_template(['story_%s_detail.html' % story.id, 'story_detail.html']). That'll allow you to use a custom template for an individual story, with a fallback template for stories that don't have custom templates.
So, when I needed something like this in the past, I'd write a view like this:
from django.http import HttpResponse
from django.template import RequestContext
from django.template.loader import select_template
def object_detail(request, object_id):
# some code to get an object
c = RequestContext(request, {'object': object})
t = select_template(['object_%s_detail.html' % object.id, 'object_detail.html'])
return HttpResponse(t.render(c))
I wanted template flexibility, and I wanted to use RequestContext, which lets me use all my context processors. I thought that meant I couldn't use the ubiquitous render_to_response shortcut.
The part I didn't notice until I spent some time nosing around in the source code is that render_to_string will call get_template if the first argument is a string, but it will call select_template if you give it a list or tuple instead.
And since render_to_response calls render_to_string, you can simply pass a list or tuple at the end of your view:
from django.shortcuts import render_to_response
from django.template import RequestContext
def object_detail(request, object_id):
# get that object
return render_to_response(
['object_%s_detail.html' % object.id, 'object_detail.html'],
{'object': object},
context_instance=RequestContext(request)
)
I'm being a bit more verbose there, but the point should be clear. That second way is a lot closer to the way I'd normally write a view, using render_to_response instead of going through building a context, selecting a template and returning an HTTP response.
That and a little template inheritance makes for a lot of flexibility without much work at all:
# object_42_detail.html
{% extends "object_detail.html" %}
{% block js %}
<script src="{{ MEDIA_URL }}/js/some-script.js"></script>
{% endblock %}
And that's pretty much it.
Comments:
Comments are closed for this post. If you still have something to say, please email me.

feb 17, 2010 at 5:37 p.m. // Adam Skory said:
Good trick, I'll remember that one.