CHAPTER 6

image

Templates

While Chapter 2 made it clear that Django is built entirely on Python, and standard Python rules apply, templates are the exception to the rule. Templates are Django’s way of generating text-based output, such as HTML or emails, where the people editing those documents may not have any experience with Python. Therefore, templates are designed to avoid using Python directly, instead favoring an extensible, easy-to-use custom language built just for Django.

By disallowing arbitrary Python expressions, templates are certainly restricted in some ways, but there are two things to keep in mind. First, the template system is backed by Python, just like everything else in Django, so it’s always possible to add Python-level code for specific features. It’s just bad form to include the actual Python code in the template itself, so Django provides other means for plugging in that extra code.

More importantly, drawing a clear line between templates and the Python code that powers them allows two separate groups of people, with different backgrounds and skill sets, to work together. For many hobbyist projects, this probably sounds like a waste, since the only people working on the site are developers. In many commercial environments, however, developers are often a separate group of people from those tasked with maintaining the site’s content and visual structure.

By clearly separating the tasks of development and template editing, it’s easy to set up an environment where developers work on the things that they’re really needed for, while content editors and designers can work on things that don’t really require development experience. Django’s templates are fairly simple in nature, and easy to pick up by most anyone, even those without any programming experience.

The basic details of what the template syntax looks like and the included tags and filters are well described elsewhere. Instead of focusing on those higher-level details, this chapter will cover how templates are loaded, parsed and rendered, how variables are managed within a template and how new tags and filters can be created. Essentially, it’s all about what developers can do to make life as easy as possible for their content editing counterparts.

What Makes a Template

Even though templates aren’t written in Python directly, they’re backed by Python, making all the good stuff possible. When a template’s code is read in from a file or other source, it’s compiled into a collection of Python objects, which are then responsible for rendering it later. Exactly how these objects work can be ignored for basic template usage, but as with anything else, a proper understanding unlocks a world of possibilities.

Taking a peek inside the django.template package, the Template class stands out as the starting point for template operations, and rightly so. When a template is loaded, its content is passed to a new instance of Template, along with some optional information about where the template itself came from. There are three arguments passed in to new Template objects, on which everything is based.

  • template_string—The only required argument, this contains the actual content of the template, as read in from a file. The great thing here is that Template accepts a string, not a filename or an open file object. By accepting just a string—either a Unicode string or a regular string encoded in UTF-8—it’s possible to set up templates from any source. Some interesting uses of this can be found in this chapter’s Applied Techniques.
  • origin—An object representing where the template came from, such as a template loader or just a raw string. It’s only used when the TEMPLATE_DEBUG setting is True, and can often be left out without penalty, but it’s always best to include it for development situations, where it can help debug problems involving multiple template loaders.
  • name—The name of the template, as passed to whatever loader requested it, if any. This is often just a relative path to the template, but could theoretically be anything that makes sense in a particular situation. After all, what Django really cares about is the template_string; the rest is just useful when debugging problems.

The actual code for Template is fairly minimal, deferring most of its work to a utility function called compile_string(), which parses the raw text, compiling it into a sequence of nodes. These nodes are just Python objects, each configured for a specific part of the template. Taken together, they represent the entire template, from start to finish, in a way that can be more easily and efficiently rendered.

These nodes are attached to the template as an attribute called nodelist. When rendering the template with data, it simply iterates over this list, rendering each node individually. This keeps the Template code very minimal, while allowing maximum flexibility. After all, if each individual theme is responsible for rendering itself, it has the full power of Python at its disposal. Therefore, creating or customizing template nodes is a simple matter of writing some real Python code.

Exceptions

All of this assumes that the template works correctly all the way through. When working with templates, there are a number of things that can go wrong, and thus a few different exceptions that could be raised. While the following exceptions are handled automatically in a way that works for most cases, it’s possible to catch these instead and handle them separately.

  • django.template.TemplateSyntaxError—The template code doesn’t validate as proper syntax, usually due to the use of an invalid tag name. This is raised immediately when trying to instantiate a Template object.
  • django.template.TemplateDoesNotExist—The requested template couldn’t be loaded by any of the known template loaders. This is issued by the template loading functions described in the “Retrieving Templates” section of this chapter.
  • django.template.TemplateEncodingError—The template string provided couldn’t be forced to a Unicode string by the Template object. Template strings must either be a Unicode string already, or be encoded in UTF-8; any other encoding must be converted to one of those two types prior to being passed to a new Template. This will be raised immediately when trying to construct a new Template object.
  • django.template.VariableDoesNotExist—A specified variable name couldn’t be resolved in the current context. See the “Context” section later in this chapter for details on this process and what situations will cause this exception to be raised.
  • django.template.InvalidTemplateLibrary—A template tag specified some invalid parameters to one of the tag library registration functions. A single tag issuing such an error will cause the entire tag library to stop loading, and none of the tags will be available to the template. This is raised when using the {% load %} template tag.

The Process at Large

Once a string is obtained from a loader, it must be converted from a single string to a set of Python objects that can be rendered. This happens automatically, and no intervention is necessary for most cases, but as with most of Django, an understanding of these internals can be quite useful. The following steps explain how a template is processed. All the classes involved live at django.template.

  1. A new Template object accepts the raw string of the template’s contents, forming the object that will be used later.
  2. A Lexer object also receives the raw template string, to begin processing the template contents.
  3. Lexer.tokenize() uses a regular expression to split the template into individual components, called tokens.
  4. These tokens populate a new Parser object.
  5. Parser.parse() goes through the available tokens, creating nodes along the way.
  6. For each block tag, Parser.parse() calls an external function that understands the tag’s syntax and returns a compiled Node for that tag.
  7. The list of compiled nodes is stored on the Template object as its nodelist attribute.

Upon completion, you’re left with a Template object that contains references to Python code, rather than the raw string that started the process. That original string is discarded after the node list is created, because those nodes contain all the necessary functionality to render the template. The Lexer, Parser, and all the Token objects are also discarded once the process completes, but they can be very useful along the way.

Content Tokens

The Lexer object is responsible for making a first pass through the template’s contents, identifying different components that are present. In addition to the template string itself, Lexer also accepts an origin, which indicates where the template came from. This processing is done by the Lexer.tokenize() method, which returns a list of Token objects. This could be seen as processing the template’s syntax, but not its semantics: individual components are identified, but they don’t yet carry much meaning.

Tokens contain all the information necessary to create nodes, but tokens themselves are relatively simple. They have just two attributes: token_type and contents. The value for Token.token_type will be one of four constants defined in django.template, while its contents will be defined by the type of token it is.

  • TOKEN_VAR—Variable tags, using the {{ var }} syntax, are placeholders for data that won’t be provided until the template is rendered. The contents attribute contains the full variable reference string, unparsed.
  • TOKEN_BLOCK—Block tags—commonly called “template tags”—use the {% name %} syntax and are populated by a Python object that can execute custom code during template rendering. The contents attribute contains the full contents of the tag, including the tag’s name and all its arguments.
  • TOKEN_COMMENT—Comment tags use a {# comment #} syntax and are essentially ignored by the template engine. Tokens are generated for them as part of the lexing process, but their contents are empty and they don’t become nodes later in the process.
  • TOKEN_TEXT—Text tokens are generated for all other content in the template, storing the text in contents.

A Lexer always gets created and utilized automatically during standard template processing, but can also be used directly. This is a useful way to inspect and analyze templates without the overhead of compiling them completely. To illustrate, consider the following example, which parses a simple one-line template into a series of tokens. Note that the token_type is printed only by value; it’s far more useful to compare this value to the constants named previously.

>>> from django.template import Lexer
>>> template = 'This is {# only #}{{ a }}{% test %}'
>>> for token in Lexer(template, 'shell').tokenize():
...     print '%s: %s' % (token.token_type, token.contents)
...
0: This is
3: only
1: a
2: test

Parsing Tokens into Nodes

Once a Lexer has split the template string into a list of tokens, those tokens are passed to a Parser, which examines them in more detail. This is the semantic side of template processing, where each token is given meaning by attaching a corresponding Node object to the template. These nodes vary greatly in complexity; comment tokens don’t produce nodes at all, text nodes have very simple nodes and block tags could have nodes that encompass the whole remainder of the template.

The Parser object itself is a bit more complicated than Lexer, because it’s responsible for more of the process. Its parse() method has to work its way through the list of tokens, identifying which tokens require nodes and which type of nodes to create along the way. Each token is retrieved and removed from the list using Parser.next_token(). That token is then used to determine what type of node to create.

For text and variable tokens, Django supplies standard nodes that are used for all instances. These are TextNode and VariableNode, respectively, and they are also available at django.template. Comment tokens are simply ignored, with no node generated at all. Block tokens go through the template tag library, matching the name of the tag with a node compilation function.

These compilation functions, described in the “Template Tags” portion of the “Adding Features for Templates” section later in this chapter, are each responsible for parsing a token’s contents and returning a Node object. Each function receives two arguments: the Parser object and the current token. Having access to the Parser object, node compilation functions can access a few additional methods to help control how much of the template that node has access to.

  • parse(parse_until=None)—This is the same method that gets called when the template is first processed, and it can also be called from within a node. By supplying a tag name for the parse_until argument, this method will return just those nodes up to that tag name. This is how tags such as block, if and for wrap around additional content between the opening and closing tags. Note that this returns fully compiled nodes.
  • next_token()—This retrieves and returns one token from the list. It also removes that token, so that future nodes don’t receive any tokens that have already been processed. Note that this returns a token that has not yet been compiled into a node.
  • skip_past(endtag)—This method is similar to parse(), accepting a tag that marks the end of where the template should be processed. The main difference is that skip_past() doesn’t parse any of the tokens into nodes along the way, nor does it return any of the tokens that were found. It simply advances the template to beyond the end tag, ignoring anything in between.

Template Nodes

While it may seem like a complicated concept, template nodes are fairly simple. All template nodes extend the basic Node class, located at django.template. In addition to an __init__() method to customize the node’s behavior, nodes have just a few methods that need to be included.

First, to maintain a common structure across all objects in a template, every template node is iterable, yielding all nodes that are contained within the node in question, rather than rendering their contents. This allows an easy way to get at all the nodes in a template.

By default, Node simply yields itself, which works well for simple template tags that just render a small snippet of text. For more complicated tags that encapsulate other content, this __iter__() should return all the nodes that were contained within it.

In addition, nodes must also provide a method called get_nodes_by_type(), though the default usually works well enough for most nodes. This method takes a single argument, nodetype, the class of node to retrieve. The node where the method was called will be checked to see if it’s an instance of that class, as well as any other nodes within it. All nodes found that are indeed instances of the specified type will be returned in a list, or an empty list will be returned if none were found.

The most important method on a node is render(), which is used to output the final text. Since rendering to text requires the data that was passed to the template, this method accepts a single argument, a context object as described in the upcoming “Context” section.

Rendering Templates

Since a template is really just a collection of compiled instructions, getting those instructions to produce output text requires a separate step. Templates can be rendered using the simple render() method, which takes a context object as its only argument.

The render() method returns a string, containing the fully-rendered output, based on the compiled nodes and the context variables. This output will often be HTML, but can be anything, since Django templates are designed to work with any text-based format. The bulk of the work of rendering gets delegated to the individual nodes themselves, with the template just iterating over all the nodes, calling render() on each in turn.

By offloading this work onto each node itself, the overall template code can be less complex, while also maximizing the flexibility of the template system in general. Since each node is fully responsible for its behavior, the possibilities are nearly limitless.

Context

A template itself is mostly just a bunch of static content, logic, and placeholders for data to be filled in later. Without having data to fill in the blanks, it’s relatively useless to a Web application. On the surface, it seems like a standard Python dictionary would suffice for this, since template variables are just names, which can be mapped to values. In fact, Django will even allow a dictionary to be used in certain cases.

One drawback of this approach is that there are some situations where a template tag might need to alter some data, and have that alteration persist for only a specific portion of the template. For example, when looping through a list, each item in the list should be available for use by other tags, but once the loop completes, that variable should no longer be accessible to the rest of the template. Beyond that, if a loop defines a variable that already had a value, that existing value should be restored once the loop finishes executing.

CONTEXTS VS. NAMESPACES

In Python, variables are assigned to namespaces, where they can later be retrieved by name, making template contexts very similar. There are also some notable differences that may cause some confusion.

Python allows namespaces to be nested, but only inside a defined class or function. In these nested namespaces, new variables aren’t accessible to the other namespaces that enclose them. Other types of code blocks, such as conditionals and loops, share the namespace with whatever code surrounds them, so new variable assignments persist after the block finishes executing. This works well because namespaces are based on where the code is written, rather than where it executes, so the programmer can easily make sure that there aren’t any conflicts with related names.

When writing a template tag, there is no way of knowing what variables will be defined in the template where the tag gets used. If it adds any new variables to the context, those could very well overwrite something else that was already set in the template. To overcome this, templates offer push() and pop() methods to allow tags to manually create a new nesting level and remove it when finished with it.

This makes templates work a bit differently from Python code in this respect, since blocks like loops essentially create a new namespace for the duration of their execution, removing it when finished. These differences may be a bit confusing at first to programmers, but designers working just with templates will only have one behavior to get used to.

To accomplish all this, Django implements its data mapping as a special Context object, which behaves much like a standard dictionary, but with some extra features. Most notably, it encapsulates a list of dictionaries internally, each of which represents a certain layer in the data map. This way, it can function like a stack as well, with the ability to push() new values onto it and pop() a layer off when no longer needed.

Neither push() nor pop() take any arguments. Instead, they simply add or remove a dictionary at the front of the list, adjusting which dictionary will be used first when looking up variables, as described next. This functionality prevents a standard dictionary from being used in most cases; it’ll work fine as long as the template is simple, but as soon as one of these tags is encountered, it’ll raise an AttributeError because it’s missing these extra methods.

Simple Variable Resolution

Looking up data in the context is one of the most basic operations, though there’s a lot that happens when a variable is referenced in a template. First, when using the standard {{ var }} syntax, Django automatically checks the context dictionaries in order from the one added most recently to the one added first. This lookup can also be performed manually on the context itself, using standard dictionary lookup syntax, which works just as well for retrieving values as setting them.

If the given name doesn’t exist in the topmost dictionary, the context falls back to the next dictionary in line, checks for the name again and the process continues. Often, the phrase “current context” is used to describe the values that are available to a template tag at any specific point in time. Even though a template will use the same context object throughout the rendering process, the current context at any given point will change depending on what tags are in use and what values are retrieved by those tags.

>>> from django.template.context import Context
>>> c = Context({'a': 1, 'b': 2})
>>> c['a'], c['b']
(1, 2)
>>> c.push()
>>> c['a'], c['b']
(1, 2)
>>> c['b'] = 3
>>> c['a'], c['b']
(1, 3)
>>> c.pop()
{'b': 3}
>>> c['a'], c['b]
(1, 2)

If it gets through all available dictionaries without finding anything, it raises a KeyError as a standard dictionary would. That KeyError is normally handled by Django directly, replacing the variable reference with a constant value defined in the site’s settings. By default, the TEMPLATE_STRING_IF_INVALID setting is set to an empty string, but this may be overridden by any site that wishes to display something different for this case.

Complex Variable Lookup

In addition to simple name lookups, variables can also contain references to certain portions of an object, using a period to separate one layer from the next. This allows a variable node to reference not just an object, but perhaps an attribute of that object, a method call or an entry in a dictionary or a list. This is also nested, so each time a dot resolves a new variable, another dot can resolve the next layer deep, such as {{ request.META.host }}.

This is handled using a separate class, appropriately named Variable. It’s instantiated with a single argument, the string to be used as the variable’s path, including any periods separating portions of the path. Once instantiated, it provides a single method, resolve(), which is used to perform all the necessary steps of retrieving the requested value. This method takes a single argument, the context where the variable should be found.

If the variable was declared with a literal value, such as a number or a quoted string, rather than a named variable, that value will always be returned directly, without even referencing the provided context. Otherwise, this resolves the first portion of the variable using the simple lookup described previously. If that part is found, it continues on to the next portion, and so on.

Each step in the chain after the first is based on the object that was retrieved in the step before it. When determining what to get at each stage, resolve() goes through a few different stages, with an error at each stage causing the lookup to continue on to the next stage.

  • Dictionary lookup—The name provided is used as a dictionary key.
  • Attribute lookup—The name is used in the standard getattr() method.
  • Method call—If the attribute lookup retrieved a callable, such as a function, that callable is executed without any arguments. If this succeeds, the return value is used, but if the function requires any arguments, it will be skipped. Also, if the function has an alters_data attribute set to True, the function will be skipped, as a security precaution.
  • List-index lookup—The variable name is coerced to an integer, if possible, and used as an index lookup to see if the value is present in a list.
>>> from django.template import Variable
>>> c = Context({'var': [1, 2, {'spam': u'eggs'}]})
>>> var = Variable('var')
>>> zero = Variable('var.0')
>>> one = Variable('var.1')
>>> spam = Variable('var.2.spam')
>>> var.resolve(c)
[1, 2, {'spam': u'eggs'}]
>>> zero.resolve(c)
1
>>> one.resolve(c)
2
>>> spam.resolve(c)
u'eggs'

Since this provides a much more robust and feature-rich way to access variables, it’s always best to use Variable when a node needs to be able to access data from a template. This will ensure that template authors have as much flexibility as possible when referencing variables, even in custom tags.

Including Aspects of the Request

It’s often necessary to include certain attributes from the incoming HTTP request, or at least to look up some other useful information based on those attributes, and include them in the template context. There’s no way for Django to magically get the request from the view into the template system, so it has to be passed in manually.

Since Context on its own only accepts a dictionary as an argument, a different object is necessary to make this happen. RequestContext, also located at django.template.context, accepts a request object as its first argument, while the normal dictionary is pushed back to the second argument instead. Aspects of the request can then be retrieved when preparing the context for use by the template.

It’s always best to use RequestContext whenever rendering a template as part of an HTTP cycle. Django’s own generic views use it consistently, and most third-party applications also use it reliably. Failing to use RequestContext may result in templates not having access to necessary data, which can cause the template to render incorrectly.

For many sites, templates might get rendered as part of an automated process, such as a nightly job to send out billing notification emails. In these situations, there is no HTTP request coming in, so RequestContext is inappropriate. Simply using a standard Context will be sufficient in these cases.

Once a RequestContext is instantiated with a request, it has to populate context variables based on attributes of that request. It doesn’t do this arbitrarily, but rather runs through code specified by another hook in Django.

Retrieving Templates

So far, all that’s been illustrated is how to work with templates once they already exist. In the real world, templates will have to be loaded on demand, according to the needs of a particular view, so there’s clearly more work to be done.

One particular requirement of retrieving templates is that they be referenced by name only, so that they can be loaded from different locations between development and production environments, without changing the code for any of the views. Chapter 8 shows how to write your own template loader, further increasing the available options. To handle this abstraction, Django provides two utility functions that should be used when retrieving templates.

django.template.loader.get_template(template_name)

Most of the time, a view knows about exactly one template, so only one name is given. The get_template() function takes the name of the requested template and returns a fully instantiated Template object. Then, that template can be rendered according to the needs of the view.

Behind the scenes, get_template() checks each template loader for the presence of a template with the given name, then returns the first one it finds. If no template was found matching the specified name, it raises a TemplateDoesNotExist exception.

django.template.loader.select_template(template_name_list)

Sometimes, it’s necessary to retrieve a template using one of a few different names. This is often the case when an application would like to provide some kind of default template every time a view is accessed, while allowing a different template to be loaded in certain cases.

Consider a real estate site, where every property listing is expected to look the same. Naturally, the view for the property listing would simply use the same standard template for every listing in the database. If, however, a property comes along that has special requirements for its listing, such as additional buyer incentives or a special notice about an urgent need to close quickly, the standard template might not have a place for that. That information might also need to be rearranged on the page for a particular listing.

To handle these cases, select_template() takes a list of template names, rather than just a single value. For each name in the list, it calls get_template() to try to retrieve it, and if that fails, it simply moves on to the next name in the list. That way, a more specific name can be supplied first—often based on an object’s ID or slug—followed by a more generic fallback.

>>> from django.template import loader
>>> t = loader.get_template('property/listing.html')
>>> t.name
'property/listing.html'>>> loader.get_template('property/listing_123.html')
Traceback (most recent call last):
  ...
django.template.TemplateDoesNotExist: property/listing_123.html
>>> t = loader.select_template(['property/listing_123.html',
                                'property/listing.html'])
>>> t.name
'property/listing.html'

In a real application, the number included in the most specific template name would be supplied by something dynamic, such as the URL being requested. That way, new property listings would use the generic template by default, but customizing an individual listing is as simple as dropping in a new template using the more specific name.

Shortcuts to Load and Render Templates

While it’s definitely nice to have full control over how templates get loaded and rendered, the common flow is to just load the template, render it with a given context, and access the resulting string. This involves a few steps, which can easily get repetitive, so Django provides a couple ways to make the process simpler.

render_to_string(template_name, dictionary=None, context_instance=None)

Living at django.templates.loader, this simple function takes a few arguments and returns a string resulting from the template rendering. A template name is retrieved according to the name provided, and is then immediately rendered by passing the given dictionary into the provided context.

If the dictionary isn’t provided, an empty dictionary is used instead, while if no context is provided, Django will simply use a Context. Most of the time, it’s most appropriate to use RequestContext, so that all context processors get applied as well. Since Django can’t magically find the request being used, a RequestContext must always be first instantiated with the request, then passed in as the context_instance.

render_to_response(template_name, dictionary=None, context_instance=None, content_type=None)

Living at django.shortcuts, this function works almost identically to render_to_string(), except that it uses the resulting string to populate an HttpResponse object, which is covered in detail in the next chapter. The only other difference is that this accepts an optional mimetype, which will be used when populating the HttpResponse.

Adding Features for Templates

Perhaps the most powerful feature of Django’s templates is the ease with which new features can be added to them, without having to modify the framework itself. Each application can provide its own set of new features, rather than expecting site developers to provide their own.

Django’s own template features can be split into two types, variables and tags, and custom add-ons fit right into those two areas. Variables can’t really be added in code, since they’re controlled by the template’s context, but variable filters are a way for applications to allow variables to be modified easily. Tags, on the other hand, can do just about anything, from adding or modifying variables in the context to branching based on variables to injecting other templates.

Setting Up the Package

In order to make things easier for template authors, Django requires your template features to live at a specific package structure within an application. The {% load %} tag uses this structure to locate a specific module among all the installed applications, without the need for complex configurations that would make life more difficult for template designers.

Any application can supply new template features by creating a templatetags package within the application’s main package. This new package can contain any number of modules, each containing a group of features that relate to each other. For example, a mail application could provide features that format text, perform basic math and show relationships between messages. The package structure would look something like this:

mail/
    __init__.py
    forms.py
    models.py
    urls.py
    views.py
    templatetags/
        __init__.py
        text.py
        math.py
        relationships.py

When writing templates for this application—or any other application you use in your site—the {% load %} tag makes those features available, accepting the names of the modules to load. These modules can come from any application in your INSTALLED_APPS setting. Django first looks for a templatetags package in each application, then looks for the module named in the {% load %} tag.

{% load text math relationships %}

Variable Filters

When variables are used in templates, they’re normally just shown exactly as they were passed into the context by the current view. Sometimes, it’s necessary to format or otherwise modify some of those values to suit the needs of a particular page. These types of presentational details are best placed in the template, so the view can just pass the raw values through, without regard to what the templates might do with them.

Django provides a number of these filters in its core distribution, intending to handle many of the most common situations you’re likely to encounter. Full documentation is available online,1 but here are a few of the most common filters:

  • capfirst—Returns a string with the first letter capitalized
  • length—Returns the number of items in a given sequence
  • date—Formats a date using a string as an argument

Filters are just Python functions that take the variable’s value as input, and return the modified value as a return value. This is really as simple as it sounds, though there is still a good bit of flexibility. Here’s what a simple filter function might look like, for displaying the first few characters of a variable, used as {{ var_name|first:3 }}.

from django.template import Library
from django.template.defaultfilters import stringfilter
 
register = Library()
 
@register.filter
@stringfilter
def first(value, count=1):
    """
    Returns the first portion of a string, according to the count provided.
    """
    return value[:count]

Accepting a Value

The first argument is the variable’s value, and is always passed in, so it should always be required by the filter function. This value is typically the variable that was passed into the template’s context, but filters can be chained together, so this value may actually be the result of another filter having already executed. Filters should therefore be made as generic as possible, accepting a wide range of input and handling it as gracefully as possible.

image Tip  This notion of “be liberal in what you accept,” has been long considered a best practice for interoperable systems. It has been documented as far back as 1980, during the formation of technologies that today’s Web is built on. The counterpart is to be “conservative in what you send,” which recommends in this case that filters should always return the same data type.

Since the value could contain data from anything the view provides, or the result from any previous filters, care should be taken when making assumptions about its type. It will often be a string, but could be a number, model instance or any number of other native Python types.

Most filters are designed to work with strings, so Django also provides a useful shortcut for dealing with those. It’s not guaranteed that the input will be a string, so string-based filters would always need to start by coercing the value to a string before continuing. There’s already a decorator for this process, called stringfilter, which is located at django.template.defaultfilters. This automatically coerces the incoming value to a string, so the filter itself doesn’t have to.

It’s also important to make sure that no changes are made to this object directly. In the event that the input is a mutable object, such as a list or a dictionary, any changes made within the filter will also be reflected in any future uses of that variable in the template. If there are any changes to be made, such as prefixing or reorganizing items, it’s essential to make a copy first, so those changes are reflected only in the filter itself.

Accepting an Argument

In addition to receiving the variable itself, it’s also possible for a filter to accept an argument to customize its use. The only change necessary to accept an argument is to define an additional argument on the function. It’s also easy to make an argument optional, simply by providing a default value in the function’s definition.

Like the variable itself, this can also be of any type, since it can either be specified as a literal or supplied through another variable. There isn’t any provided decorator for coercing this value to a string, as numbers are very common as filter arguments. Whatever argument your filter expects, just make sure to explicitly coerce it to the type it needs, and always catch any exceptions that might occur during that coercion.

Returning a Value

The vast majority of the time, a filter should return a string, since it’s expected to be sent into the output of the rendered template. There is definite use for filters that return other types of data, such as a filter that averages the numbers in a list, returning the result as a number, but those are far less common, and should be well-documented in case other filters are chained with them, to avoid unexpected results.

More important is the fact that filters should always return a value. If anything goes wrong during the filter’s processing, no exceptions should be raised. This also means that if the filter calls some other function that might raise an exception, the exception should be handled by the filter so it doesn’t raise up beyond that. In the event of any of these problems, the filter should either return the original input or an empty string; which one to use will depend on the purposes of the filter in question.

Registering As a Filter

Once the function is all written up, it’s registered with Django by using the Library class provided at django.template. Once instantiated, Library has a filter() method that can be used as a decorator, which, when applied to the filter function, automatically registers it with Django. That’s all that’s necessary on the code side.

This doesn’t make it globally available to all templates by default, but rather just tells Django that the application provides it. Any template that would like to use it must still load the application’s template features using the {% load %} tag.

Template Tags

Filters serve a very useful and practical purpose, but since they can only receive up to two values—the variable and an argument—and can only return one value, it’s easy to see how quickly an application can outgrow them. Getting more functionality requires the use of template tags, which allow just about anything.

Like filters, Django provides a number of tags in its core distribution, which are documented online. Some of the more common tags are listed here, along with a brief description of their functionality.

  • for—Allows a template to loop over the items in a sequence
  • filter—Applies a template filter, such as those described previously, to all the content contained within the tag
  • now—Prints out the current time, using some optional arguments to format it

Template tags are implemented as a pairing of a function and a Node class, with the former configuring the latter. The node is just like the nodes described earlier, representing the compiled structure of the template tag. The function, on the other hand, is used to accept the various allowable syntax options for the tag and to instantiate the node accordingly.

A Simple Tag

The simplest form of tag only exists in and of itself, typically to inject additional content into the page, based on some arguments. The node for this case is extremely simple, just taking and storing those arguments and formatting them into a string during rendering. Here’s how it would look if the filter from the previous section were instead implemented as a tag. In the template, this would look like {% first var_name 3 %}.

from django.template import Library, Node, Variable
 
register = Library()
 
class FirstNode(Node):
    def __init__(self, var, count):
        self.var = var
        self.count = count
 
    def render(self, context):
        value = self.var.resolve(context)
        return value[:self.count]

The function to compile it, on the other hand, is a bit more complicated. Tag functions, unlike filter functions, always take two arguments: the template parser object, and a token representing the text contained within the tag. It’s up to the compilation function to extract the necessary bits of information from these two objects.

For a simple tag like this, it’s not necessary to worry about the parser object, but the token is still necessary in order to get the argument, if one was specified.

The most important thing to know about the token is the split_contents() method, which intelligently breaks apart a tag’s declaration into individual components, including the tag’s name and its arguments.

It correctly handles variable references, quoted strings and numbers, though it doesn’t do any variable resolution, and leaves the quotes around quoted strings.

In order to get the two necessary bits of information out of our template tag, token.split_contents() is used to extract them from the declared string. Then, these can be coerced to the correct types and used to instantiate the node described previously.

@register.tag
def first(parser, token):
    var, count = token.split_contents()[1:]
    return FirstNode(Variable(var), int(count))

A Shortcut for Simple Tags

Thankfully, there’s a shortcut that makes this process a whole lot easier. The Library object contains another decorator method, simple_tag(), which handles simpler cases like this. Behind the scenes, it handles the parsing and resolution of arguments, and even the creation of the node class, so all that’s left for the template tag is a single function that looks quite similar to a variable filter.

from django.template import Library

 
register = Library()
 
@register.simple_tag
def first(value, count):
    return value[:count]

This is still of limited use, but there are many such situations where a simpler tag is necessary, and the shortcut can become quite a time-saver. For more advanced needs, manually creating the node offers much more power and flexibility.

Adding Features to All Templates

Django doesn’t automatically load all applications’ template filters and tags by default; instead, it uses just a default set for all templates. For those templates that need to access a specific application’s template features and using the {% load %} tag is too much overhead, there’s also an option where an application can be added to the default set of tags for all templates.

Also living at django.template, the add_to_builtins() function takes the name of the application to be included by default. Specifically, this is the app_label for that application, as described in Chapter 3. Once an application is supplied to this function, all of its tags and filters will be made available to all templates. This can be called anywhere that will be executed when the application loads, such as its __init__.py module.

This should be used sparingly, as it does incur some added overhead for even those templates that don’t use any of that application’s features. In some cases, however, it’s necessary to override the behavior of default filters or tags, and add_to_builtins() provides that option. Keep in mind that more than one application can do this, so there’s still no guarantee which application’s version of a particular feature will be used. Django will simply overwrite them as they’re encountered, so the last application to load is what will be used. Use it with care.

Applied Techniques

Django templates are designed to make things as easy as possible for the people who have to write templates on a regular basis. Advanced techniques are used to reinforce this idea, simplifying tasks that might otherwise be too complex to perform in a template. An application will often have its own unique template needs and should provide tags and filters to satisfy them. Even better is to provide features that can be reused by other applications if necessary.

Embedding Another Template Engine

While Django’s template engine is suitable for most common cases, its limitations may cause frustration in situations where more power or flexibility is needed. Template tags extend Django’s functionality, but only by involving programmers to write them for each individual need.

Another template engine with a different design philosophy might be more suitable to some of these needs. By allowing template designers to switch to an alternative engine for portions of templates, additional functionality is exposed without requiring additional programming. Tags can still simplify common tasks, but switching template engines can be an easy way to support corner cases.

One such alternative template engine is Jinja,2 which has a syntax fairly similar to Django. There are fundamental differences in the design philosophies, making Jinja a better choice for situations where the output requires complex conditions and logic. These aspects make it a perfect candidate for embedding within Django templates.

To illustrate this, consider a template that needs to calculate a composite value to display in a template. This feature isn’t available in Django, so it would ordinarily require a custom template tag or a view that calculated the value before sending it to the template.

{% load jinja %}
 
{% for property in object_list %}
Address: {{ property.address }}
Internal area: {{ property.square_feet }} square feet
Lot size: {{ property.lot_width }}' by {{ property.lot_depth }}'
 
{% jinja %}
Lot area: {{ property.lot_width * property.lot_depth / 43560 }} acres
{% endjinja %}
{% endfor %}

Django will automatically process everything up to the jinja tag, passing all remaining tokens to the Jinja compilation function along with the Parser object. The parser and tokens can be used to extract the content written between the jinja and endjinja tags. This then needs to be converted back into string content before being passed to Jinja for rendering.

Converting a Token to a String

Before diving into the full compilation function, first notice that tokens must be converted back into strings for Jinja to process them. Jinja uses a fairly similar syntax for its templates, so Django’s Lexer accurately identifies variable, block and comment tags. Though Jinja also creates tokens for those tags, tokens from the two template engines aren’t compatible with each other, so they must be converted back to strings. Jinja can then process them as it would any template from any source.

To accomplish this conversion, the node compilation function will rely on a separate function, which takes a token and returns a string. It works on the fact that django.template also contains constants for the beginning and ending portions of these tags. With this information and the structure of tokens, a suitable string can be created from a given token.

from django import template
 
def string_from_token(token):
    """
    Converts a lexer token back into a string for use with Jinja.
    """
    if token.token_type == template.TOKEN_TEXT:
        return token.contents
 
    elif token.token_type == template.TOKEN_VAR:
        return '%s %s %s' % (
            template.VARIABLE_TAG_START,
            token.contents,
            template.VARIABLE_TAG_END,
        )
 
    elif token.token_type == template.TOKEN_BLOCK:
        return '%s %s %s' % (
            template.BLOCK_TAG_START,
            token.contents,
            template.BLOCK_TAG_END,
        )
 
    elif token.token_type == template.TOKEN_COMMENT:
        return u'' # Django doesn't store the content of comments

This won’t produce an exact replica of the original template string. Some whitespace gets removed during Django’s Lexer processing and comments lose their contents entirely. All functional aspects of the tags are retained, so the template will still work as advertised, but know that some minor formatting issues may arise as the result of this technique.

Compiling to a Node

With a function in place to reproduce strings for the tokens within the jinja block, the next step is to generate a Node that will be used to render the content along with the rest of the template. When gathering up the content between an opening tag and its closing tag, compilation functions often make use of the Parser.parse() method, passing in the name of the end tag, which will return a list of Node objects representing the inner content.

Since Jinja tags can’t be processed using Django’s node functions, Parser.parse() will cause problems due to incorrect syntax. Instead, the Jinja compilation function must access the tokens directly, which can then be converted back to strings. There are no provided functions to do this entirely, but combining Parser.next_token() with some extra logic will work quite well.

The compilation function can loop over the available tokens, calling Parser.next_token() each time. This loop will execute until either an endjinja block token is found or there are no more tokens in the template. Once a token is obtained from the parser, it can be converted to a string and added to an internal template string that can be used to populate a JinjaNode.

import jinja2
from django import template
from django.base import TemplateSyntaxError
 
register = template.Library()
 
def jinja(parser, token):
    """
    Define a block that gets rendered by Jinja, rather than Django's templates.
    """
    bits = token.contents.split()
    if len(bits) != 1:
        raise TemplateSyntaxError("'%s' tag doesn't take any arguments." % bits[0])
 
    # Manually collect tokens for the tag's content, so Django's template
    # parser doesn't try to make sense of it.
    contents = []
    while 1:
        try:
            token = parser.next_token()
        except IndexError:
            # Reached the end of the template without finding the end tag
            raise TemplateSyntaxError("'endjinja' tag is required.")
        if token.token_type == template.TOKEN_BLOCK and
           token.contents == 'endjinja':
            break
        contents.append(string_from_token(token))
    contents = ''.join(contents)
 
    return JinjaNode(jinja2.Template(contents))
jinja = register.tag(jinja)

image Caution  By not using the parser’s parse() method, you won’t be able to use any other Django tags inside of the {% jinja %} tag. That’s not a problem here, since the contents are processed by Jinja instead, but using this technique without a good reason can cause problems with other types of tags.

Preparing the Jinja Template

Once the compilation function retrieves the Jinja template contents from the Django template tokens, a JinjaNode is created to access that template. Jinja provides its own Template object that compiles content into tangible objects, so it makes sense to use it when a JinjaNode is created.

Then, when it comes time to render the JinjaNode, all it takes is to render the compiled Jinja template and return that output back to Django’s template. This task is trickier than it may seem on the surface, because Django’s Context objects, which contain variables that should be passed to Jinja, don’t behave entirely like Python dictionaries. They support the common dictionary-style syntax for accessing keys, but internally, their structure is quite different from what Jinja expects.

To pass a nested Context object to the Jinja template properly, it must first be flattened to a single, standard Python dictionary. This can be done fairly easily, simply by looping through the individual dictionaries stored in the context and assigning them to a new dictionary, maintaining the precedence that Django itself uses: the first appearance of a particular key takes priority over any other instances of that same key. Only if a key doesn’t exist in the new Jinja context dictionary should it be added, so that no existing values get overwritten in the process.

Once the dictionary is available, that data can be passed to Jinja’s own Template.render() method. The result from that method is the properly rendered content that can be returned from the JinjaNode.render(), placing that content in the page.

import jinja2
 
class JinjaNode(template.Node):
    def __init__(self, template):
        self.template = template
 
    def render(self, django_context):
        # Jinja can't use Django's Context objects, so we have to
        # flatten it out to a single dictionary before using it.
        jinja_context = {}
        for layer in django_context:
            for key, value in layer.items():
                if key not in jinja_context:
                    jinja_context[key] = value
        return self.template.render(jinja_context)

Enabling User-Submitted Themes

Earlier in this chapter, we discovered that templates can be loaded from any source, as long as there’s an appropriate loader that knows how to retrieve them. One shortcoming of that approach is that it’s only valid for loading templates for everybody; there’s no way of associating templates with a specific user.

That’s not really a failure in any way, since most applications would need it to work exactly as it does. Also, user information is only available once a request comes in, so there wouldn’t be any way to access it in a generic fashion. Every tool has its time, and there are certainly times where it’s useful to have templates tied to users.

Consider a site where users are encouraged to customize their own experience, by supplying custom themes that will be used while they’re logged in. This gives users a great deal of control over how they engage in the site, and can pull them further into the experience. This can be enhanced still further if they’re given the opportunity for their own custom themes to be made available for others to use. This idea isn’t good for all sites, but for heavily community-oriented sites, especially those in artistic circles, it can be a great boost to the user experience.

A WORD ABOUT ADVERTISING

Many sites on the Web today are funded at least in part by advertisements placed on their various pages. This advertising only works if it’s actually shown to users, so they have a chance to click on ads and buy products or services. By introducing user-editable themes to a site, users have a perfect opportunity to remove any ads a site may rely on, so it’s important to carefully consider whether this is right for your site.

Any themes that a site’s staff approves for the use of the site’s general audience can be checked first to ensure that they don’t cause any harm to the advertising on the site, or to the site’s own branding. This is a great way to enforce at least some quality control on the process. The problem is that users can create themes to behave however they like, prior to submitting them for approval, and may use them on their own through the site, removing ads from their own experiences.

One way to minimize the impact of this problem is to offer paid site memberships, with one of the benefits being the ability to create custom themes. This way, unpaid users will always see advertising as a way of funding their use of the site, while paid users are offsetting their lack of advertising with an annual fee.

In fact, if your site adopts this model, it’s best to remove ads for paid users altogether, regardless of what theme they’re using. Nobody likes paying for the use of a site, only to still be presented with advertising designed to bring further revenue to that same site.

On the surface, it may seem like this is a perfect job for Cascading Style Sheets (CSS). CSS is all about the presentation of Web sites, but it’s always limited by the ordering of content on a page. For example, markup placed higher in the document is difficult to place at the bottom of a page, and vice versa. By allowing users to edit the template that determines those positions, it’s easy to unlock many more possibilities.

Using Django templates poses some technical and security challenges that must be overcome, and solving these challenges exposes a number of interesting ways to use templates. First, consider the problems that need to be solved.

  • If templates are to be edited, they should be stored in the database.
  • Templates need to be tied to a specific user, to restrict them from editing everything, and also for assigning credit to the proper authors when themes get promoted.
  • Users can’t have access to the full range of the Django template language. That’s a security risk that would expose way too much information to just anyone.
  • Themes must be approved by staff members prior to being made available for use by everyone.
  • Once themes are submitted for approval, and after they’ve been approved, users shouldn’t be able to make any changes.
  • A user’s personal theme—whether personally created or selected from the work of others—should be used on all portions of the site.
  • In addition to the template itself, each theme should have a CSS file associated with it, to better style other aspects of the site.

That’s quite a list of things that need to be covered, and individual sites may have even more requirements. It’s not quite as bad as it may seem on the surface, as Django already has many things in place to make those problems easy to solve.

Setting Up the Models

The first order of business is to make a place for templates to be stored in the database. In standard Django fashion, this is done with a model, with fields for the various properties of the template. For this application, a theme consists of a few various pieces of information:

  • A block of text to be used as the content of the template
  • A URL to a CSS file
  • A user who created it
  • A title, so other users can easily reference it, should it be made available for everyone
  • An indication of whether it’s the site-wide default, so that users who haven’t yet selected a theme still have one to use

Most of this information will only be used by the theme object itself, as only the main block of text will be passed in to the template. It’s easy to think of a theme as a template in its own right, where it’s simultaneously a set of data that gets stored in the database and a set of instructions that are used to render HTML. Python provides a way to make that notion explicit and offers a simple way to deal with themes.

By using multiple inheritance, it’s possible for a theme to be both a model and a template, behaving in whichever way is necessary for the task at hand. The class inherits from django.db.models.Model and django.template.Template, and __init__() is overridden to initialize both sides separately:

from django.db import models
from django import template
from django.contrib.auth.models import User
 
from themes.managers import ThemeManager
 
class Theme(models.Model, template.Template):
    EDITING, PENDING, APPROVED = range(3)
    STATUS_CHOICES = (
        (EDITING, u'Editing'),
        (PENDING, u'Pending Approval'),
        (APPROVED, u'Approved'),
    )
    author = models.ForeignKey(User, related_name='authored_themes')
    title = models.CharField(max_length=255)
    template_string = models.TextField()
    css = models.URLField(null=True, blank=True)
    status = models.SmallIntegerField(choices=STATUS_CHOICES, default=EDITING)
    is_default = models.BooleanField()
 
    objects = ThemeManager()
 
    def __init__(self, *args, **kwargs):
        # super() won't work here, because the two __init__()
        # method signatures accept different sets of arguments
        models.Model.__init__(self, *args, **kwargs)
        template.Template.__init__(self, self.template_string,
                                   origin=repr(self), name=unicode(self))
    def save(self):
        if self.is_default:
            # Since only one theme can be the site-wide default, any new model that
            # is defined as default must remove the default setting from any other
            # theme before committing to the database.
            self.objects.all().update(is_default=False)
        super(Theme, self).save()
 
    def __unicode__(self):
        return self.title

That’s enough to get the themes themselves stored in the database, but it still doesn’t cover how a user can select a theme to use while browsing the site. Ordinarily, that would be set up as a ForeignKey on the model that references Theme, but since the User model is outside our control, something else will need to be done.

One way to store user-centric information, such as preferences, is to add a custom user model. Django’s official documentation3 covers this in detail, but the basic idea is that you can provide your own model to use in place of Django’s own user model. Your custom model can contain additional fields for anything user-related, including selected themes. A site can only have one custom user model, though, and it makes little sense to hijack that feature solely for the purpose of supporting themes. Instead, we can use a ManyToManyField to connect it to the User model.

class Theme(models.Model):
    ...  # All the other fields shown above
    users = models.ManyToManyField(User, through='SelectedTheme')
    ...  # All the other methods shown above
 
class SelectedTheme(models.Model):
    user = models.OneToOneField(User)
    theme = models.ForeignKey(Theme)

By using a OneToOneField, we can ensure that each user only appears once in the intermediary table. That way, each user can only have one selected theme. There are also some utility functions that can help manage this behavior a bit. There are actually two different methods that would be useful here, both for getting themes based on the user. One is for retrieving a user’s selected theme, while the other is for retrieving the themes a user has created.

from django.db import models
from django.conf import settings
 
class ThemeManager(models.Manager):
    def by_author(self, user):
        """
        A convenience method for retrieving the themes a user has authored.
        Since the only time we'll be retrieving themes by author is when
        they're being edited, this also limits the query to those themes
        that haven't yet been submitted for review.
        """
        return self.filter(author=self, status=self.model.EDITING)
 
    def get_current_theme(self, user):
        return SelectedTheme.objects.get(user=user).theme

With this manager in place, it’s easy to retrieve themes for a specific user, both those that user can edit, and the one that user should use when browsing the site. Having these shortcuts in place helps make views simpler, allowing them to focus on the business they really have to do. The whole point of a site-wide theme is that it’s used for every view, so clearly something else needs to be done to accommodate that.

Supporting Site-Wide Themes

Individual views have enough to worry about, and shouldn’t be responsible for managing themes. Instead, there needs to be a way to retrieve a user’s selected theme—or the default—and have that automatically applied to whatever template a view uses. Ideally, all this should happen without any changes to the views, so there’s little extra work that needs to be done.

This is a job best suited for a context processor, a concept described earlier in this chapter. By using a context processor, every view that uses RequestContext will automatically have access to the proper theme. This makes the ordinarily good advice of always using RequestContext now an absolute requirement. As will be seen in the next section, templates will explicitly rely on the theme being available, and failing to use RequestContext will violate that assumption.

The context processor required for this process is fairly straightforward, but it has to provide a few specific features. It must determine whether the current user is logged in or not, identify the user’s selected theme, fall back to a default theme if no theme is selected or if the user isn’t logged in, and it must return the proper theme so that it may be added to the template’s context. This code would be placed in a module called context_processors.py, in keeping with the conventions used within Django itself.

from django.conf import settings
 
from themes.models import Theme
def theme(request):
    if hasattr(request, 'user') and request.user.is_authenticated():
        # A valid user is logged in, so use the manager method
        theme = Theme.objects.get_current_theme(request.user)
    else:
        # The user isn't logged in, so fall back to the default
        theme = Theme.objects.get(is_default=True)
    name = getattr(settings, 'THEME_CONTEXT_NAME', 'theme')
    return {name: theme}

Note the use of hasattr() here in the test to see whether a user is logged in. That may seem unnecessary, but by adding that simple condition to the test, it allows this context processor to be used with no middleware requirements. Otherwise, it would always require django.contrib.auth.middleware.AuthenticationMiddleware, which places the user attribute on the request. If that middleware isn’t in use, every user will simply receive the default theme.

Also, note that the name of the context variable is driven by another new setting, this time called THEME_CONTEXT_NAME. This defaults to 'theme', so that it’s not necessary to supply a name explicitly unless that causes a clash with some other feature. This is a bit of a recurring theme (pun intended), because with an application that has to interact with a good deal outside of itself, such as user models and template contexts, it’s important to make sure conflicts are kept to a minimum.

With this file in place, the only thing left is to add 'themes.context_processors.theme' to the TEMPLATE_CONTEXT_PROCESSORS setting to make sure it gets applied to all the templates. Once the theme is made available to the template, it’s still necessary to make sure the template can access it and make use of it.

Setting Up Templates to Use Themes

The end goal of themes is to reorder the components of a page, so it’s important to identify what a “component” is. In terms of Django templates, this would mean a block of markup, identified by the {% block %} template tag. Each component of a page could be defined in a separate block, separating each bit into its own space.

With Django’s template inheritance, it’s possible to define blocks in one template that will be filled in with content from another template. This way, a page-specific template can define what goes in each block, while a base template can specify where those blocks are rendered, and what other markup gets placed around them. This would be an excellent way to reorder significant portions of a page, as long as there’s a way to dynamically specify where the base template places all the blocks.

Django supports template inheritance through the {% extends %} tag, which takes a single argument to identify the base template to extend. Typically, this is a hard-coded name of the template to use as a base. It can also take a context variable, containing a string to use as this base template. If that context variable points to a template instance, Django will use that instead of bothering to look up a template anywhere else.

Taking advantage of this in a template is easy; just put {% extends theme %} at the top of the template. If you’ve specified a THEME_CONTEXT_NAME explicitly for your site, make sure to change theme to whatever you’ve entered for that setting. That still only covers part of it. It’s still necessary to get the templates to make use of the blocks defined in the theme.

There’s no universal way to do this, since each site will have its own template inheritance setup, and its own set of blocks that every page will need to fill in. Typically, these blocks would be used for things like page title, navigation, page content and footers, but different sites may have different needs.

In addition, a site may have more blocks that can’t be rearranged, but are instead defined inside of other blocks. These wouldn’t be taken into consideration for our purposes at the moment, since themes are only concerned with blocks that can be moved around. Consider an application with the following blocks that can be customized:

  • logo—The site’s logo, as an image
  • title—The title of the current page
  • search—A search box, possibly with advanced options
  • navigation—A collection of links or other interface used for getting around the site
  • sidebar—A bit of content related to the current page
  • content—The flesh of the current page, whether that be a product listing, press release, search results or contact form
  • footer—A copyright disclaimer, along with a few links for job openings, investor relations and contact information

Every theme must define all of these blocks in order to make sure the whole site gets displayed, so it’s important to outline them explicitly. Every template on the site needs to define content to be placed into these blocks, so that there’s always something to put in the right places. Many of those blocks aren’t specified to any particular page, so template inheritance comes to the rescue here as well.

By placing another template layer between the theme and the individual page, some blocks can be populated automatically for all pages, while others are left for individual pages to fill in. The individual page template still has final authority, with the ability to override any block with new content, if necessary. That just leaves the issue of making sure that templates do in fact define all the blocks required by the site’s inheritance scheme.

Validating and Securing Themes

Any time a site accepts input from users, it must be scrutinized to make sure that it fulfills a certain set of requirements and stays within acceptable limits. Themes are no exception there, but user-editable templates also represent a very real security risk. Django takes steps to ensure that templates can’t execute any common functions that make changes to the database, but there are a number of other things a template can do.

By default, only Django’s own data-altering methods are secured from templates by using the alters_data attribute. Any application’s models may define other methods that make changes to the database, and if those aren’t marked with alters_data, they’re fair game for use in templates. Even read-only access, if not kept in check, can be a problem. A theme is used on every page, and many pages will have access to a wide array of objects through model relationships.

There are so many ways to access things that should be kept private that no blacklist approach can ever hope to be complete. Instead, a whitelist approach is necessary, where themes are only allowed to use a small subset of features provided by Django’s template system. The trick is determining the right way to approach a problem like this.

On the surface, it may seem like regular expressions are the way to go. After all, Django itself uses a regular expression to parse templates and break them up into nodes, so surely it would be trivial to write a more limited expression to secure templates. That may be true for now, but remember that Django is constantly improving, and the future may bring new syntax to templates.

However unlikely that may be, if it does happen, no amount of careful crafting of our regular expression can predict what new syntax might be included in the future. Anything that slips past this protection has the potential to harm the site or divulge confidential information. That’s a lot to pin on the hope that the template syntax will remain constant.

Instead, we’ll rely on Django’s own regular expression to compile the template into a list of nodes, just like normal. Then, once it’s been compiled to a nodelist, it’s easy to peek at those nodes to make sure they’re all doing the right thing. Using this, forms can easily verify that the template defines all the right blocks and nothing else. Theme templates must:

  • Inherit from the template referenced by the THEME_EXTENDS setting.
  • Provide one block with the name referenced by a THEME_CONTAINER_BLOCK setting.
  • Populate that block with all the blocks referenced in the THEME_BLOCKS setting.
  • Provide no content in any of the THEME_BLOCKS blocks.
  • Provide no other blocks than those mentioned in the THEME_BLOCKS setting.
  • Contain no other tags whatsoever, only text.
from django import forms
from django import template
from django.template.loader_tags import BlockNode, ExtendsNode
from django.conf import settings
from theme import models
 
class ThemeForm(forms.ModelForm):
    title = forms.CharField()
    body = forms.CharField(widget=forms.Textarea)
 
    def clean_body(self):
        try:
            tpl = template.Template(self.cleaned_data['body'])
        except template.TemplateSyntaxError as e:
            # The template is invalid, which is an input error.
            raise forms.ValidationError(unicode(e))
 
        if [type(n) for n in tpl.nodelist] != [ExtendsNode] or
            tpl.nodelist[0].parent_name != settings.THEME_EXTENDS:
                # No 'extends' tag was found
                error_msg = u"Template must extend '%s'" % settings.THEME_EXTENDS
                raise forms.ValidationError(error_msg)
 
        if [type(n) for n in tpl.nodelist[0].nodelist] != [BlockNode] or
            tpl.nodelist[0].nodelist[0].name != settings.THEME_CONTAINER_BLOCK:
                # Didn't find exactly one block tag with the required name
                error_msg = u"Theme needs exactly one '%s' block" %
                            settings.THEME_CONTAINER_BLOCK
                raise forms.ValidationError(error_msg)
 
        required_blocks = list(settings.THEME_BLOCKS[:])
        for node in tpl.nodelist[0].nodelist[0].nodelist:
            if type(node) is BlockNode:
                if node.name not in required_blocks:
                    error_msg = u"'%s' is not valid for themes." % node.name)
                    raise forms.ValidationError(error_msg)
                required_blocks.remove(node.name)
                if node.nodelist:
                    error_msg = u"'%s' block must be empty." % node.name)
                    raise forms.ValidationError(error_msg)
            elif type(node) is template.TextNode:
                # Text nodes between blocks are acceptable.
                pass
            else:
                # All other tags, including variables, are invalid.
                error_msg = u"Only 'extends', 'block' and plain text are allowed."
                raise forms.ValidationError(error_msg)
 
        if required_blocks:
            # Some blocks were missing from the template.
            blocks = ', '.join(map(repr, required_blocks))
            error_msg =  u"The following blocks must be defined: %s" % blocks
            raise forms.ValidationError(error_msg)
 
    class Meta:
        model = models.Theme

An Example Theme

Even with an application in place, it may be difficult to understand how a theme would be written to work with the site. Consider a site using this themes application with the following settings:

THEME_EXTENDS = 'base.html'
THEME_CONTEXT_NAME = 'theme'
THEME_CONTAINER_BLOCK = 'theme'
THEME_BLOCKS = (
    'title',
    'sidebar',
    'links',
)

The base.html template at the root of the inheritance chain might look like this:

<html>
<head>
<title>{% block title %}{% endblock %}</title>
<link rel="stylesheet" type="text/css" href="/style.css"/>
</head>
<body>{% block theme %}{% endblock %}</body>
</html>

A theme can then be written to fill in the application’s requirements: extend from base.html, provide a theme block and fill it with empty title, sidebar and links blocks. Unlike the other templates, this code would be stored in the database, as an instance of the Theme model.

{% extends 'base.html' %}
 
{% block theme %}
<h1>{% block title %}{% endblock %}</h1>
<ul id="links">{% block links %}{% endblock %}</ul>
<div id="content">{% block content %}{% endblock %}</div>
{% endblock %}

Now, individual templates for the rest of the site can be written to extend from the theme variable and fill in the title, sidebar and links blocks. Consider the template for the root of a real estate site:

{% extends theme %}
 
{% block title %}Acme Real Estate{% endblock %}
 
{% block links %}
<li><a href="{% url home_page %}">Home</a></li>
<li><a href="{% url property_list %}">Properties</a></li>
<li><a href="{% url about_page %}">About</a></li>
{% endblock %}
 
{% block content %}
<p>Welcome to Acme Real Estate!</p>
{% endblock %}

With all of these templates in place, loading up the root of the site will yield a full HTML document like the following:

<html>
<head>
<title>Acme Real Estate</title>
<link rel="stylesheet" type="text/css" href="/style.css"/>
</head>
<body>
<h1>Acme Real Estate</h1>
<ul id="links">
<li><a href="/">Home</a></li>
<li><a href="/properties/">Properties</a></li>
<li><a href="/about/">About</a></li>
</ul>
<div id="content">
<p>Welcome to Acme Real Estate!</p>
</div>
</body>
</html>

Now What?

Views and templates combine to determine what content should be sent to users, but it still has to make its way to the browser. Django speaks HTTP fluently, so there are a number of ways to customize that journey.

1 http://prodjango.com/tags/

2 http://prodjango.com/jinja/

3 http://prodjango.com/custom-user/

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset