Simple Django-like Templating

October 2006 | Fredrik Lundh

I’m currently working on a new version of the ludicrously simple
content management system I’m using for zone.effbot.org, and
wanted a template system that was a bit more flexible than the old
replace-based hack I’d been using.

I ended up implementing a small subset of the
Django template
syntax (dead link), partially because the syntax is pretty okay, partially because
the Django’s template rendering model provides a separation between templates
and code that has worked very well in earlier projects. (But mostly
because it’s fun to play with minimalistic reimplementations of larger
designs. Do you really need all that code?)

Here’s a small Django template (a simplified version of an example
from the Django documentation):

The {{ … }} sections are Django variable markers, which are
replaced with the specified content when the template is rendered.

The variables are looked up in a given rendering context, and the dot
notation is used as a general access mechanism; what looks like an
attribute can be either a key in a dictionary, an attribute, the
result of a method call, or, for numeric attributes, an item in a sequence.
This makes it easy to tweak the underlying data model, without having
to change the templates.

(Django also supports {% … %} syntax for blocks, but that’s outside
the scope of this article. At least this version of it.)

To locate the Django variables, you need to parse the template. Since
the format is really simple, parsing it is pretty simple too; you can
create a token stream simply by splitting the template on “{{” and “}}”
markers, and then use an
iterator-based parser
approach to do the actual parsing.

Note that the render function returns a list of string fragments.
Since we’re going to write this to a pipe or a file anyway, there’s
really no need to turn this into a single string object. Just use
writelines instead of write, and you’re done.

The next step is to implement the variable function.
Django resolves variable names against a given context object, where
each part of the name is interpreted as either:

Dictionary item (object[key]), or

Attribute (object.key), or

Method (object.key()), or

Sequence item (object[int(key)])

in that order.

In other words, when Django sees section.title, it first
looks for section[“title”], then for section.title, then
for section.title(), and finally for
section[int(“title”)]. If none of these work, the result is
(usually) set to an empty string.

The following variable function implements the first three;
if you need support for sequence items, extending this function should
be straightforward.

Django also supports variable filters, which are listed after the
actual variable definition, and separated from the variables and from
each other by vertical bars (|).

The version above ignored all filters. Here’s a somewhat enhanced
version that supports a single optional filter (from the set of filters
in the FILTERS dictionary). This version still ignores unknown filters,
but that’s good enough for my current purposes:

With all this in place, you can quickly parse and render a template
with a single call to the render function. The following example
introduces two simple classes, and uses a dictionary as the context
object.

Note that the current implementation parses the template and generates
the output in a single step. That’s good enough for one-shot generation of
static web pages, or for use from CGI scripts, but for heavier use, you may
want to cache and reuse the parsed representation in one way or another.