Pages

Thursday, March 24, 2011

Custom Template Loaders in Django

Django’s built-in template loaders (described in the “Inside Template Loading” section above) will usually cover all your template-loading needs, but it’s pretty easy to write your own if you need special loading logic. For example, you could load templates from a database, or directly from a Subversion repository using Subversion’s Python bindings, or (as shown shortly) from a ZIP archive.

A template loader—that is, each entry in the TEMPLATE_LOADERS setting —is expected to be a callable with this interface:

load_template_source(template_name, template_dirs=None)

The template_name argument is the name of the template to load (as passed to loader.get_template() orloader.select_template()), and template_dirs is an optional list of directories to search instead ofTEMPLATE_DIRS.

If a loader is able to successfully load a template, it should return a tuple:(template_source, template_path). Here, template_source is the template string that will be compiled by the template engine, and template_path is the path the template was loaded from. That path might be shown to the user for debugging purposes, so it should quickly identify where the template was loaded from.

If the loader is unable to load a template, it should raise django.template.TemplateDoesNotExist.
Each loader function should also have an is_usable function attribute. This is a Boolean that informs the template engine whether this loader is available in the current Python installation. For example, the eggs loader (which is capable of loading templates from Python eggs) sets is_usable to False if thepkg_resources module isn’t installed, because pkg_resources is necessary to read data from eggs.
An example should help clarify all of this. Here’s a template loader function that can load templates from a ZIP file. It uses a custom setting, TEMPLATE_ZIP_FILES, as a search path instead of TEMPLATE_DIRS, and it expects each item on that path to be a ZIP file containing templates:

import zipfile
from django.conf import settings
from django.template import TemplateDoesNotExist

def load_template_source(template_name, template_dirs=None):
"""Template loader that loads templates from a ZIP file."""

template_zipfiles = getattr(settings, "TEMPLATE_ZIP_FILES", [])

# Try each ZIP file in TEMPLATE_ZIP_FILES.
for fname in template_zipfiles:
try:
z = zipfile.ZipFile(fname)
source = z.read(template_name)
except (IOError, KeyError):
continue
z.close()
# We found a template, so return the source.
template_path = "%s:%s" % (fname, template_name)
return (source, template_path)

# If we reach here, the template couldn't be loaded
raise TemplateDoesNotExist(template_name)

# This loader is always usable (since zipfile is included with Python)
load_template_source.is_usable = True

The only step left if we want to use this loader is to add it to the TEMPLATE_LOADERS setting. If we put this code in a package called mysite.zip_loader, then we add mysite.zip_loader.load_template_source toTEMPLATE_LOADERS.

No comments:

Post a Comment