The Django Book

You're reading an outdated version of this book; a newer version is available.

Chapter 4: The Django template system

In the previous chapter, you may have noticed something peculiar in how we returned the HTML in our example views. Namely, the HTML was hard-coded directly in our Python code!

This arrangement leads to several problems:

  • Obviously, any change to the design of the page would require a change to the Python code. The design of a site tends to change far more frequently than the underlying Python code, so it would be convenient if the frequency of HTML changes were separated from changes to Python code.
  • Second, writing backend Python code and designing/coding HTML are two different disciplines, and most professional Web development environments split these responsibilities across separate people (or even separate departments). Designers and HTML/CSS coders shouldn’t have to edit Python code to get their job done; they should deal with HTML.
  • Similarly, it’s most efficient if programmers can work on Python code and designers can work on templates at the same time, rather than one person waiting for the other to finish editing a single file that contains both Python and HTML.

For these reasons, it’s much cleaner and more maintainable to separate the design of the page from the Python code itself. We can do this with Django’s template system.

Template system basics

A Django template is a string of text that is intended to separate the presentation of a document from its data. A template defines placeholders and various bits of basic logic — tags — that regulate how the document should be displayed. Usually, templates are used for outputting HTML, but Django templates are equally capable of generating any text-based format.

Let’s dive in with a simple example template. This template describes an HTML page that thanks a person for making an order from a company. Think of it as a form letter:

<html>
<head><title>Ordering notice</title></head>

<body>

<p>Dear {{ person_name }},</p>

<p>Thanks for placing an order from {{ company }}. It's scheduled to
ship on {{ ship_date|date:"F j, Y" }}.</p>

<p>Here are the items you've ordered:</p>

<ul>
{% for item in item_list %}
<li>{{ item }}</li>
{% endfor %}
</ul>

{% if ordered_warranty %}
<p>Your warranty information will be included in the packaging.</p>
{% endif %}

<p>Sincerely,<br />{{ company }}</p>

</body>
</html>

This template is basic HTML with some variables and template tags thrown in. Let’s step through it:

  • Any text surrounded by a pair of braces — e.g., {{ person_name }} — is a variable. This means “insert the value of the variable with the given name.” (How do we specify the values of the variables? We’ll get to that in a moment.)

  • Any text that’s surrounded by curly braces and percent signs — e.g., {% if ordered_warranty %} — is a block tag. The definition of a block tag is quite broad: A block tag just tells the template system to do something.

    This example template contains two block tags — the {% for item in item_list %} tag (a “for” tag) and the {% if ordered_warranty %} tag (an “if” tag). A “for” tag acts as a simple loop construct, letting you loop over each item in a sequence. An “if” tag, as you may expect, acts as a logical “if” statement. In this particular case, the tag checks whether the value of the ordered_warranty variable evaluates to True. If it does, the template system will display everything between the {% if ordered_warranty %} and {% endif %}. If not, the template system won’t display it. The template system also supports {% else %} and other various logic statements.

    Each Django template has access to several built-in block tags. In addition, you can write your own tags.

  • Finally, the second paragraph of this template has an example of a filter. Filters are a way to alter the display of a variable. In this example — {{ ship_date|date:"F j, Y" }} — we’re passing the ship_date variable to the date filter, giving the date filter an argument "F j, Y". The date filter formats dates in a given format, as specified by that argument. Filters are attached using a pipe character (|), as a reference to Unix pipes.

    Each Django template has access to several built-in filters. In addition, you can write your own filters.

Using the template system

To use the template system in Python code, just follow these two steps:

  • First, create a Template object by providing the raw template code as a string. Django also offers a way to create Template objects by designating the path to a template file on the filesystem; we’ll see that in a bit.
  • Then, call the render() method of the Template object with a given set of variables — the context. This returns a fully rendered template, as a string, with all of the variables and block tags evaluated according to the context.

Creating template objects

The easiest way to create a Template object is to instantiate it directly. The Template class lives in the django.template module, and the constructor takes one argument, the raw template code. Let’s dip into the Python interactive interpreter to see how this works in code. (Type python at the command line to start the interactive interpreter.) Here’s a basic walkthrough:

>>> from django.template import Template
>>> t = Template("My name is {{ my_name }}.")
>>> print t

If you’re following along interactively, you’ll see something like this after typing print t:

<django.template.Template object at 0xb7d5f24c>

That 0xb7d5f24c will be different every time, and it doesn’t really matter; it’s simply the Python “identity” of the Template object.

Interactive interpreter examples

Throughout this book, we’ll feature example Python interactive interpreter sessions. You can recognize these examples by spotting the triple greater-than signs (>>>), which designate the interpreter’s prompt. If you’re copying examples from this book, don’t copy those greater-than signs.

Multiline statements in the interactive interpreter are padded with three dots (...). For example:

>>> print """This is a
... string that spans
... three lines."""
This is a
string that spans
three lines.
>>> def my_function(value):
...     print value
>>> my_function('hello')
hello

Those three dots at the start of the additional lines are inserted by the Python shell — they’re not part of our input. We include them here to be faithful to the actual output of the interpreter. If you copy our examples to follow along, don’t copy those dots.

When you create a Template object, the template system compiles the raw template code into an internal, optimized form, ready for rendering. But if your template code includes any syntax errors, the call to Template() will cause a TemplateSyntaxError exception:

>>> from django.template import Template
>>> t = Template('{% notatag %} ')
Traceback (most recent call last):
  File "<stdin>", line 1, in ?
  ...
  django.template.TemplateSyntaxError: Invalid block tag: 'notatag'

The system raises a TemplateSyntaxError exception for any of the following cases:

  • Invalid block tags
  • Invalid arguments to valid block tags
  • Invalid filters
  • Invalid arguments to valid filters
  • Invalid template syntax
  • Unclosed block tags (for block tags that require closing tags)

Rendering a template

Once you have a Template object, you can pass it data by giving it a context. A context is simply a set of variables and their associated values. A template uses this to populate its variable tags and evaluate its block tags.

A context is represented in Python by the Context class, which lives in the django.template module. Its constructor takes one optional argument: a dictionary mapping variable names to variable values. Call the Template object’s render() method with the context to “fill” the template. For example:

>>> from django.template import Context, Template
>>> t = Template("My name is {{ name }}.")
>>> c = Context({"name": "Stephane"})
>>> t.render(c)
'My name is Stephane.'

Variable names must begin with a letter (A-Z or a-z) and may contain digits, underscores and dots. (Dots are a special case we’ll get to in a moment.) Variable names are case sensitive.

Here’s an example of template compilation and rendering, using the sample template from the beginning of this chapter:

>>> from django.template import Template, Context
>>> raw_template = """<p>Dear {{ person_name }},</p>
...
... <p>Thanks for ordering {{ product }} from {{ company }}. It's scheduled to
... ship on {{ ship_date|date:"F j, Y" }}.</p>
...
... {% if ordered_warranty %}
... <p>Your warranty information will be included in the packaging.</p>
... {% endif %}
...
... <p>Sincerely,<br />{{ company }}</p>"""
>>> t = Template(raw_template)
>>> import datetime
>>> c = Context({'person_name': 'John Smith',
...     'product': 'Super Lawn Mower',
...     'company': 'Outdoor Equipment',
...     'ship_date': datetime.date(2009, 4, 2),
...     'ordered_warranty': True})
>>> t.render(c)
"<p>Dear John Smith,</p>\n\n<p>Thanks for ordering Super Lawn Mower from Outdoor Equipment.
It's scheduled to ship on April 2, 2009.</p>\n\n<p>Your warranty information will be included
in the packaging.</p>\n\n\n<p>Sincerely,<br />Outdoor Equipment</p>"

Let’s step through this one statement at a time:

  • First, we import the classes Template and Context, which both live in the module django.template.

  • Next, we save the raw text of our template into the variable raw_template. Note that we use a triple quote marks to designate the string, because it wraps over multiple lines; strings designated with single quote marks cannot be wrapped over multiple lines.

  • Next, we create a template object t by passing raw_template to the Template class constructor.

  • Then we import the datetime module from Python’s standard library, because we’ll need it in the following statement.

  • Next, we create a context object c. The Context constructor takes a Python dictionary mapping variable names to values. Here, for example, we specify that the person_name is 'John Smith', product is 'Super Lawn Mower', etc.

  • Finally, we call the render() method on our template object, passing it the context. This returns the rendered template — that is, it replaces template variables with the actual values of the variables, and it executes any block tags.

    Note that the warranty paragraph was displayed because the ordered_warranty variable evaluated to True. Also note the date, April 2, 2009, which is displayed according to the format string 'F j, Y'. (We’ll explain format strings for the date filter shortly.)

    If you’re new to Python, you may wonder why this output includes newline characters ('\n') rather than displaying the line breaks. That’s happening because of a subtlety in the Python interactive interpreter: The call to t.render(c) returns a string, and by default the interactive interpreter displays the representation of the string, rather than the printed value of the string. If you want to see the string with line breaks displayed as true line breaks rather than '\n' characters, use the print statement: print t.render(c).

Those are the fundamentals of using the Django template system — just write a template, create a template object, create a context and call the render() method.

Multiple contexts, same template

Once you have a template object, you can render multiple contexts through it. For example:

>>> from django.template import Template, Context
>>> t = Template('Hello, {{ name }}')
>>> print t.render(Context({'name': 'John'}))
Hello, John
>>> print t.render(Context({'name': 'Julie'}))
Hello, Julie
>>> print t.render(Context({'name': 'Pat'}))
Hello, Pat

Whenever you’re using the same template to render multiple contexts like this, it’s most efficient to create the Template object once, then call render() on it multiple times. For example:

# Bad
for name in ('John', 'Julie', 'Pat'):
    t = Template('Hello, {{ name }}')
    print t.render(Context({'name': name}))

# Good
t = Template('Hello, {{ name }}')
for name in ('John', 'Julie', 'Pat'):
    print t.render(Context({'name': name}))

Django’s template parsing is quite fast. Behind the scenes, most of the parsing happens via a single call to a short regular expression. This is a stark contrast to XML-based templating engines, which incur the overhead of an XML parser and tend to be orders of magnitude slower than Django’s template rendering engine.

Context variable lookup

In the examples so far, we’ve passed simple values in the template contexts — mostly strings, plus a datetime.date example. However, the template system elegantly handles more complex data structures, such as lists, dictionaries and custom objects.

The key to traversing complex data structures in Django templates is the dot (.) character. Use a dot to access dictionary keys, attributes, indices or methods of an object.

This is best illustrated with a few examples. First, say you’re passing a Python dictionary to a template. To access the values of that dictionary by dictionary key, use a dot:

>>> from django.template import Template, Context
>>> person = {'name': 'Sally', 'age': '43'}
>>> t = Template('{{ person.name }} is {{ person.age }} years old.')
>>> c = Context({'person': person})
>>> t.render(c)
'Sally is 43 years old.'

Similarly, dots also allow access of object attributes. For example, a Python datetime.date object has year, month and day attributes, and you can use a dot to access those attributes in a Django template:

>>> from django.template import Template, Context
>>> import datetime
>>> d = datetime.date(1993, 5, 2)
>>> d.year
1993
>>> d.month
5
>>> d.day
2
>>> t = Template('The month is {{ date.month }} and the year is {{ date.year }}.')
>>> c = Context({'date': d})
>>> t.render(c)
'The month is 5 and the year is 1993.'

This example uses a custom class:

>>> from django.template import Template, Context
>>> class Person(object):
...     def __init__(self, first_name, last_name):
...         self.first_name, self.last_name = first_name, last_name
>>> t = Template('Hello, {{ person.first_name }} {{ person.last_name }}.')
>>> c = Context({'person': Person('John', 'Smith')})
>>> t.render(c)
'Hello, John Smith.'

Dots are also used to access list indices. For example:

>>> from django.template import Template, Context
>>> t = Template('Item 2 is {{ items.2 }}.')
>>> c = Context({'items': ['apples', 'bananas', 'carrots']})
>>> t.render(c)
'Item 2 is carrots.'

Negative list indices are not allowed. For example, the template variable {{ items.-1 }} would cause a TemplateSyntaxError.

Finally, dots are also used to call methods on objects. For example, each Python string has the methods upper() and isdigit(), and you can call those in Django templates using the same dot syntax:

>>> from django.template import Template, Context
>>> t = Template('{{ var }} -- {{ var.upper }} -- {{ var.isdigit }}')
>>> t.render(Context({'var': 'hello'}))
'hello -- HELLO -- False'
>>> t.render(Context({'var': '123'}))
'123 -- 123 -- True'

Note that, in the method calls, you don’t include parentheses. Also, it’s not possible to pass arguments to the methods; you can only call methods that have no required arguments. (We’ll explain this philosophy later in this chapter.)

The dot lookups can be summarized like this: When the template system encounters a dot in a variable name, it tries the following lookups, in this order:

  • Dictionary lookup. Example: foo["bar"]
  • Attribute lookup. Example: foo.bar
  • Method call. Example: foo.bar()
  • List-index lookup. Example: foo[bar]

The system uses the first lookup type that works. It’s short-circuit logic.

Dot lookups can be nested multiple levels deep. For instance, the following example uses {{ person.name.upper }}, which translates into a dictionary lookup (person['name']), then a method call (upper()):

>>> from django.template import Template, Context
>>> person = {'name': 'Sally', 'age': '43'}
>>> t = Template('{{ person.name.upper }} is {{ person.age }} years old.')
>>> c = Context({'person': person})
>>> t.render(c)
'SALLY is 43 years old.'
A word about method calls

Method calls are slightly more complex than the other lookup types. Here are some things to keep in mind:

  • If, during the method lookup, a method raises an exception, the exception will be propagated, unless the exception has an attribute silent_variable_failure whose value is True. If the exception does have a silent_variable_failure attribute, the variable will render as an empty string. For example:

    >>> t = Template("My name is {{ person.first_name }}.")
    >>> class PersonClass3:
    ...     def first_name(self):
    ...         raise AssertionError, "foo"
    >>> p = PersonClass3()
    >>> t.render(Context({"person": p}))
    Traceback (most recent call last):
    ...
    AssertionError: foo
    
    >>> class SilentAssertionError(AssertionError):
    ...     silent_variable_failure = True
    >>> class PersonClass4:
    ...     def first_name(self):
    ...         raise SilentAssertionError
    >>> p = PersonClass4()
    >>> t.render(Context({"person": p}))
    "My name is ."
    
  • A method call will only work if the method has no required arguments. Otherwise, the system will move to the next lookup type (list-index lookup).

  • Obviously, some methods have side effects, and it’d be foolish at best, and possibly even a security hole, to allow the template system to access them.

    Say, for instance, you have a BankAccount object that has a delete() method. The template system shouldn’t be allowed to do something like this:

    I will now delete this valuable data. {{ account.delete }}
    

    To prevent this, set a function attribute alters_data on the method. The template system won’t execute a method if the method has alters_data=True set. For example:

    def delete(self):
        # Delete the account
    delete.alters_data = True
    
How invalid variables are handled

By default, if a variable doesn’t exist, the template system renders it as an empty string, failing silently. For example:

>>> from django.template import Template, Context
>>> t = Template('Your name is {{ name }}.')
>>> t.render(Context())
'Your name is .'
>>> t.render(Context({'var': 'hello'}))
'Your name is .'
>>> t.render(Context({'NAME': 'hello'}))
'Your name is .'
>>> t.render(Context({'Name': 'hello'}))
'Your name is .'

The system fails silently rather than raising an exception because it’s intended to be resilient to human error. In the real world, it’s unacceptable for a Web site to become inaccessible due to a small template syntax error.

Note that it’s possible to change Django’s default behavior in this regard, by tweaking a setting in your Django configuration. We’ll discuss this in Chapter 10, “Extending the template engine.”

Playing with Context objects

Most of the time, you’ll instantiate Context objects by passing in a fully-populated dictionary to Context(). But you can add and delete items from a Context object once it’s been instantiated, too, using standard Python dictionary syntax:

>>> from django.template import Context
>>> c = Context({"foo": "bar"})
>>> c['foo']
'bar'
>>> del c['foo']
>>> c['foo']
''
>>> c['newvariable'] = 'hello'
>>> c['newvariable']
'hello'

A Context object is a stack. That is, you can push() and pop() it. If you pop() too much, it’ll raise django.template.ContextPopException:

>>> c = Context()
>>> c['foo'] = 'first level'
>>> c.push()
>>> c['foo'] = 'second level'
>>> c['foo']
'second level'
>>> c.pop()
>>> c['foo']
'first level'
>>> c['foo'] = 'overwritten'
>>> c['foo']
'overwritten'
>>> c.pop()
Traceback (most recent call last):
...
django.template.ContextPopException

Using a Context as a stack comes in handy in some custom template tags, as you’ll see in Chapter 10.

Basic template tags and filters

As we’ve mentioned already, the template system ships with built-in tags and filters. Here’s a rundown of the most common ones. Appendix 6 includes a full list of all built-in tags and filters, and it’s a good idea to familiarize yourself with that list to have an idea of what’s possible.

if/else

The {% if %} tag evaluates a variable, and if that variable is “true” (i.e., it exists, is not empty, and is not a false boolean value), the system will display everything between {% if %} and {% endif %}. For example:

{% if today_is_weekend %}
    <p>Welcome to the weekend!</p>
{% endif %}

An {% else %} tag is optional:

{% if today_is_weekend %}
    <p>Welcome to the weekend!</p>
{% else %}
    <p>Get back to work.</p>
{% endif %}

The {% if %} tag accepts and, or or not for testing multiple variables, or to negate a given variable. For example:

{% if athlete_list and coach_list %}
    Both athletes and coaches are available.
{% endif %}

{% if not athlete_list %}
    There are no athletes.
{% endif %}

{% if athlete_list or coach_list %}
    There are some athletes or some coaches.
{% endif %}

{% if not athlete_list or coach_list %}
    There are no athletes or there are some coaches (OK, so
    writing English translations of boolean logic sounds
    stupid; it's not our fault).
{% endif %}

{% if athlete_list and not coach_list %}
    There are some athletes and absolutely no coaches.
{% endif %}

{% if %} tags don’t allow and and or clauses within the same tag, because the order of logic would be ambiguous. For example, this is invalid:

{% if athlete_list and coach_list or cheerleader_list %}

If you need to combine and and or to do advanced logic, just use nested {% if %} tags. For example:

{% if athlete_list %}
    {% if coach_list or cheerleader_list %}
        We have athletes, and either coaches or cheerleaders!
    {% endif %}
{% endif %}

Multiple uses of the same logical operator are fine, as long as you use the same operator. For example, this is valid:

{% if athlete_list or coach_list or parent_list or teacher_list %}

There is no {% elif %} tag. Use nested {% if %} tags to accomplish the same thing:

{% if athlete_list %}
    <p>Here are the athletes: {{ athlete_list }}.</p>
{% else %}
    <p>No athletes are available.</p>
    {% if coach_list %}
        <p>Here are the coaches: {{ coach_list }}.</p>
    {% endif %}
{% endif %}

Make sure to close each {% if %} with an {% endif %}. Otherwise, Django will throw a TemplateSyntaxError.

for

The {% for %} tag allows you to loop over each item in a sequence. As in Python’s for statement, the syntax is for X in Y, where Y is the sequence to loop over and X is the name of the variable to use for a particular cycle of the loop. Each time through the loop, the template system will render everything between {% for %} and {% endfor %}.

For example, to display a list of athletes given a variable athlete_list:

<ul>
{% for athlete in athlete_list %}
    <li>{{ athlete.name }}</li>
{% endfor %}
</ul>

Add reversed to the tag to loop over the list in reverse:

{% for athlete in athlete_list reversed %}
...
{% endfor %}

It’s possible to nest {% for %} tags:

{% for country in countries %}
    <h1>{{ country.name }}</h1>
    <ul>
    {% for city in country.city_list %}
        <li>{{ city }}</li>
    {% endfor %}
    </ul>
{% endfor %}

There is no support for “breaking” out of a loop before the loop is finished. If you want to accomplish this, change the variable you’re looping over so that it only includes the values you want to loop over. Similarly, there is no support for a “continue” statement that would instruct the loop processor to return immediately to the front of the loop. (See “Philosophies and limitations” later in this chapter for the reasoning behind this design decision.)

The {% for %} tag sets a magic forloop template variable within the loop. This variable has a few attributes that give you information about the progress of the loop:

  • forloop.counter is always set to an integer representing the number of times the loop has been entered. This is one-indexed, so the first time through the loop, forloop.counter will be set to 1. Example:

    {% for item in todo_list %}
        <p>{{ forloop.counter }}: {{ item }}</p>
    {% endfor %}
    
  • forloop.counter0 is like forloop.counter, except it’s zero-indexed. Its value will be set to 0 the first time through the loop.

  • forloop.revcounter is always set to an integer representing the number of remaining items in the loop. The first time through the loop, forloop.revcounter will be set to the total number of items in the sequence you’re traversing. The last time through the loop, forloop.revcounter will be set to 1.

  • forloop.revcounter0 is like forloop.revcounter, except it’s zero-indexed. The first time through the loop, forloop.revcounter0 will be set to the number of elements in the sequence minus one. The last time through the loop, it will be set to 0.

  • forloop.first is a boolean value set to True if this is the first time through the loop. This is convenient for special-casing:

    {% for object in objects %}
        {% if forloop.first %}<li class="first">{% else %}<li>{% endif %}
        {{ object }}
        </li>
    {% endfor %}
    
  • forloop.last is a boolean value set to True if this is the last time through the loop. An example use for this would be to put pipe characters between a list of links:

    {% for link in links %}{{ link }}{% if not forloop.last %} | {% endif %}{% endfor %}
    
  • forloop.parentloop is a reference to the forloop object for the parent loop, in case of nested loops. For example:

    {% for country in countries %}
        <table>
        {% for city in country.city_list %}
            <tr>
            <td>Country #{{ forloop.parentloop.counter }}</td>
            <td>City #{{ forloop.counter }}</td>
            <td>{{ city }}</td>
            </tr>
        {% endfor %}
        </table>
    {% endfor %}
    

The magic forloop variable is only available within loops. After the template parser has reached {% endfor %}, forloop disappears.

If your template context already contains a variable called forloop, Django will override it within {% for %} tags. In other, non-loop parts of the template, your forloop will still be available and unchanged. We advise against setting template variables with the name forloop, but if you need to do this and want to access your custom forloop from within a {% for %} tag, you can use forloop.parentloop, described above.

ifequal/ifnotequal

The Django template system deliberately is not a full-fledged programming language and, thus, does not allow you to execute arbitrary Python statements. (More on this in “Philosophies and limitations” below.) However, it’s quite a common template requirement to compare two values and display something if they’re equal — and Django provides an {% ifequal %} tag for that purpose.

The {% ifequal %} tag compares two values and displays everything between {% ifequal %} and {% endifequal %} if the values are equal.

This example compares the template variables user and currentuser:

{% ifequal user currentuser %}
    <h1>Welcome!</h1>
{% endifequal %}

The arguments can be hard-coded strings, with either single or double quotes, so the following is valid:

{% ifequal section 'sitenews' %}
    <h1>Site News</h1>
{% endifequal %}

{% ifequal section "community" %}
    <h1>Community</h1>
{% endifequal %}

Just like {% if %}, the {% ifequal %} tag supports an optional {% else %}:

{% ifequal section 'sitenews' %}
    <h1>Site News</h1>
{% else %}
    <h1>No News Here</h1>
{% endifequal %}

Only template variables, strings, integers and decimal numbers are allowed as arguments to {% ifequal %}. These are valid examples:

{% ifequal variable 1 %}
{% ifequal variable 1.23 %}
{% ifequal variable 'foo' %}
{% ifequal variable "foo" %}

Any other types of variables, such as Python dictionaries, lists or booleans, can not be hard-coded in {% ifequal %}. These are invalid examples:

{% ifequal variable True %}
{% ifequal variable [1, 2, 3] %}
{% ifequal variable {'key': 'value'} %}

If you need to test whether something is true or false, use the {% if %} tags instead of {% ifequal %}.

Comments

Just as in HTML or in a programming language such as Python, the Django template language allows for comments. To designate a comment, use {# #}. For example:

{# This is a comment #}

Comment will not be output when the template is rendered.

A comment cannot span multiple lines. In the following template, the rendered output will look exactly the same as the template (i.e., the comment tag will not be parsed as a comment):

This is a {# comment goes here
and spans another line #}
test.

Filters

As explained earlier in this chapter, template filters are simple ways of altering the value of variables before they’re displayed.

Filters look like this:

{{ name|lower }}

This displays the value of the {{ name }} variable after being filtered through the lower filter, which converts text to lowercase. Use a pipe (|) to apply a filter.

Filters can be chained — that is, the output of one filter is applied to the next. Here’s a common idiom for escaping text contents, then converting line breaks to <p> tags:

{{ my_text|escape|linebreaks }}

Some filters take arguments. A filter argument looks like this:

{{ bio|truncatewords:"30" }}

This displays the first 30 words of the bio variable. Filter arguments always are in double quotes.

Here are a few of the most important filters:

  • addslashes — Adds a backslash before any backslash, single quote or double quote. This is useful if you’re outputting some text into a JavaScript string.

  • date — Formats a date or datetime object according to a format string given in the parameter. For example:

    {{ pub_date|date:"F j, Y" }}
    

    Format strings are defined in Appendix 6.

  • escape — Escapes ampersands, quotes and angle brackets in the given string. This is useful for sanitizing user-submitted data and for ensuring data is valid XML or XHTML. Specifically, escape makes these conversions:

    • Converts & to &amp;
    • Converts < to &lt;
    • Converts > to &gt;
    • Converts " (double quote) to &quot;
    • Converts ' (single quote) to &#39;
  • length — Returns the length of the value. You can use this on a list or a string, or any Python object that knows how to determine its length (i.e., any object that has a __len__() method).

Philosophies and limitations

Now that you’ve gotten a feel for the Django template language, we should point out some of its intentional limitations, along with some philosophies on why it works the way it works.

More than any other component of Web applications, programmer opinions on template systems vary wildly — a statement supported by the fact that Python alone has dozens, if not hundreds, of open-source template-language implementations, each inevitably created because its developer deemed all existing template languages inadequate. (In fact, it is said to be a rite of passage for a Python developer to write his or her own template language! And if you haven’t done this yet, consider it. It’s a fun exercise.)

With that in mind, the first Django philosophy to point out is that Django doesn’t require that you use its template language. Because Django is intended to be a full-stack Web framework that provides all the pieces necessary to be a productive Web developer, many times it’s more convenient to use Django’s template system than other Python template libraries, but it’s not a strict requirement in any sense. As we’ll see in the section “Using templates in views” below, it’s very easy to use another template language with Django — almost as easy as to use Django’s template language.

Still, it’s clear we have a strong preference for the way Django’s template language works. The template system has roots in how Web development is done at World Online and the combined experience of Django’s creators. Here are a few of those philosophies:

  • Business logic should be separated from presentation logic. We see a template system as a tool that controls presentation and presentation-related logic — and that’s it. The template system shouldn’t support functionality that goes beyond this basic goal.

    For that reason, it’s impossible to call Python code directly within Django templates. All “programming” is fundamentally limited to the scope of what template tags can do. It is possible to write custom template tags that do arbitrary things, but the out-of-the-box Django template tags intentionally do not allow for arbitrary Python code execution.

  • Syntax should be decoupled from HTML/XML. Although Django’s template system is used primarily to output HTML, it’s intended to be just as usable for non-HTML formats, such as plain text. Some other template languages are XML-based, placing all template logic within XML tags or attributes, but Django deliberately avoids this limitation. Requiring valid XML to write templates introduces a world of human mistakes and hard-to-understand error messages, and using an XML engine to parse templates incurs an unacceptable level of overhead in template processing.

  • Designers are assumed to be comfortable with HTML code. The template system isn’t designed so that templates necessarily are displayed nicely in WYSIWYG editors such as Dreamweaver. That is too severe of a limitation and wouldn’t allow the syntax to be as nice as it is. Django expects template authors are comfortable editing HTML directly.

  • Designers are assumed not to be Python programmers. The template system authors recognize that Web page templates are most often written by designers, not programmers, and therefore should not assume Python knowledge.

    However, the system also intends to accomodate small teams in which the templates are created by Python programmers. It offers a way to extend the system’s syntax by writing raw Python code. (More on this in Chapter 10.)

  • The goal is not to invent a programming language. The goal is to offer just enough programming-esque functionality, such as branching and looping, that is essential for making presentation-related decisions.

As a result of these design philosophies, the Django template language has the following limitations:

  • A template cannot set a variable or change the value of a variable. It’s possible to write custom template tags that accomplish these goals (see Chapter 10), but the stock Django template tags do not allow it.
  • A template cannot call raw Python code. There’s no way to “drop into Python mode” or use raw Python constructs. Again, it’s possible to write custom template tags to do this, but the stock Django template tags don’t allow it.

Using templates in views

We’ve learned the basics of using the template system; now, let’s integrate this into a view. Recall the current_datetime view from the previous chapter. Here’s what it looked like:

from django.http import HttpResponse
import datetime

def current_datetime(request):
    now = datetime.datetime.now()
    html = "<html><body>It is now %s.</body></html>" % now
    return HttpResponse(html)

Let’s change this view to use Django’s template system. At first, you might think to do something like this:

from django.template import Template, Context
from django.http import HttpResponse
import datetime

def current_datetime(request):
    now = datetime.datetime.now()
    t = Template("<html><body>It is now {{ current_date }}.</body></html>")
    html = t.render(Context({'current_date': now}))
    return HttpResponse(html)

Sure, that uses the template system, but it doesn’t solve the problems we pointed out in the introduction of this chapter. Namely, the template is still embedded in the Python code. Let’s fix that by putting the template in a separate file, which this view will load.

The simple, “dumb” way to do this would be to save your template somewhere on your filesystem and use Python’s built-in file-opening functionality to read the contents of the template. Here’s what that might look like, assuming the template was saved as the file /home/djangouser/templates/mytemplate.html:

from django.template import Template, Context
from django.http import HttpResponse
import datetime

def current_datetime(request):
    now = datetime.datetime.now()
    # Simple, "dumb" way of saving templates on the filesystem.
    # This doesn't account for missing files!
    fp = open('/home/djangouser/templates/mytemplate.html')
    t = Template(fp.read())
    fp.close()
    html = t.render(Context({'current_date': now}))
    return HttpResponse(html)

This approach, however, is inelegant for these reasons:

  • For one, it doesn’t handle the case of a missing file. If the file mytemplate.html doesn’t exist or isn’t readable, the open() call would raise an IOError exception.
  • Second, it hard-codes your template location. If you were to use this technique for every view function, you’d be duplicating the template locations. Not to mention that’s a lot of typing!
  • Third, it includes a lot of boring boilerplate code. The calls to open(), fp.read() and fp.close() require a lot of typing and not much creativity.

To solve these issues, we’ll use template loading and template directories.

Template loading

Django provides a convenient and powerful API for loading templates from disk, with the goal of removing redundancy both in your template-loading calls and in your templates themselves.

In order to use this template-loading API, first you’ll need to tell the framework where you store your templates. The place to do this is in your settings file.

A Django settings file is the place to put configuration for your Django instance (aka your Django project). It’s a simple Python module with module-level variables, one for each setting.

When you ran django-admin.py startproject mysite in Chapter 2, the script created a default settings file for you, aptly named settings.py. Have a look at the file’s contents. It contains variables that look like this (though not necessarily in this order):

DEBUG = True
TIME_ZONE = 'America/Chicago'
USE_I18N = True
ROOT_URLCONF = 'mysite.urls'

This is pretty self-explanatory; the settings and their respective values are simple Python variables. And because the settings file is just a plain Python module, you can do dynamic things such as checking the value of one variable before setting another. (This also means that you should avoid Python syntax errors in your settings file.)

We’ll cover settings files in depth later in this book, but for now, have a look at the TEMPLATE_DIRS setting. This setting tells Django’s template loading mechanism where to look for templates. By default, it’s an empty tuple. Pick a directory where you’d like to store your templates, and add it to TEMPLATE_DIRS, like so:

TEMPLATE_DIRS = (
    '/home/django/mysite/templates',
)

A few things to note:

  • You can specify any directory you want, as long as the directory and templates within that directory are readable by the user account under which your Web server runs. If you can’t think of an obvious place to put your templates, we recommend creating a templates directory within your Django project (i.e., within the mysite directory you created in Chapter 2, if you’ve been following along with our examples).

  • Don’t forget the comma at the end of the template-directory string! Python requires commas within single-element tuples to disambiguate the tuple from a parenthetical statement. This is a common newbie gotcha.

    If you want to avoid this error, you can make TEMPLATE_DIRS a list instead of a tuple, because single-element lists don’t require a trailing comma:

    TEMPLATE_DIRS = [
        '/home/django/mysite/templates'
    ]
    

    A tuple is slightly more efficient than a list, though, so we recommend using a tuple for your TEMPLATE_DIRS setting.

  • It’s simplest to use absolute paths, i.e. directory paths that start at the root of the filesystem. If you want to be a bit more flexible and decoupled, though, you can take advantage of the fact that Django settings files are just Python code by constructing the contents of TEMPLATE_DIRS dynamically. For example:

    import os.path
    
    TEMPLATE_DIRS = (
        os.path.join(os.path.basename(__file__), 'templates'),
    )
    

    This example uses the “magic” Python variable __file__, which is automatically set to the filename of the Python module in which the code lives.

  • If you’re on Windows, include your drive letter and use Unix-style forward slashes rather than backslashes. For example:

    TEMPLATE_DIRS = (
        'C:/www/django/templates',
    )
    

With TEMPLATE_DIRS set, the next step is to change the view code to use Django’s template-loading functionality rather than hard-coding the template paths. Returning to our current_datetime view, let’s change it like so:

from django.template.loader import get_template
from django.template import Context
from django.http import HttpResponse
import datetime

def current_datetime(request):
    now = datetime.datetime.now()
    t = get_template('current_datetime.html')
    html = t.render(Context({'current_date': now}))
    return HttpResponse(html)

In this example, we’re using the function django.template.loader.get_template() rather than loading the template from the filesystem manually. The get_template() function takes a template name as its argument, figures out where the template lives on the filesystem, opens that file and returns a compiled Template object.

If get_template() cannot find the template with the given name, it raises a TemplateDoesNotExist exception. To see what that looks like, fire up the Django development server again, as in Chapter 3, by running python manage.py runserver within your Django project’s directory. Then, point your browser at the page that activates the current_datetime view (e.g., http://127.0.0.1:8000/now/). Assuming your DEBUG setting is set to True and you haven’t yet created a current_datetime.html template, you should see a Django error page highlighting the TemplateDoesNotExist error.

(We’ll have a screenshot here.)

This error page is similar to the one we explained in Chapter 3, with one additional piece of debugging information: a “Template-loader postmortem” section. This section tells you which templates Django tried to load, along with the reason each attempt failed (e.g., “File does not exist”). This information is invaluable when you’re trying to debug template-loading errors.

As you can probably tell by looking at the error messages, Django attempted to look for a template by combining the directory in your TEMPLATE_DIRS setting with the template name you passed to get_template(). So if your TEMPLATE_DIRS contained '/home/django/templates', it would look for the file '/home/django/templates/current_datetime.html'.

Moving along, create the current_datetime.html file within your template directory, using the following template code:

<html><body>It is now {{ current_date }}.</body></html>

Refresh the page in your Web browser, and you should see the fully rendered page.

render_to_response()

Because it’s such a common idiom to load a template, fill a Context and return an HttpResponse object with the result of the rendered template, Django provides a shortcut that lets you do those things in one line of code. This shortcut is a function called render_to_response(), which lives in the module django.shortcuts. Most of the time, you’ll be using render_to_response() rather than loading templates and creating Context and HttpResponse objects manually.

Here’s the ongoing current_datetime example rewritten to use render_to_response():

from django.shortcuts import render_to_response
import datetime

def current_datetime(request):
    now = datetime.datetime.now()
    return render_to_response('current_datetime.html', {'current_date': now})

What a difference! Let’s step through the code changes:

  • We no longer have to import get_template, Template, Context or HttpResponse. Instead, we import django.shortcuts.render_to_response. The import datetime remains.
  • Within the current_datetime function, we still calculate now, but the template loading, context creation, template rendering and HttpResponse creation is all taken care of by the render_to_response() call. Because render_to_response() returns an HttpResponse object, we can simply return that value in the view.

The first argument to render_to_response() should be the name of the template to use, relative to your template directory. The second argument, if given, should be a dictionary to use in creating a Context for that template. If you don’t provide a second argument, render_to_response() will use an empty dictionary.

The locals() trick

Consider our latest incarnation of current_datetime:

def current_datetime(request):
    now = datetime.datetime.now()
    return render_to_response('current_datetime.html', {'current_date': now})

Many times, as in this example, you’ll find yourself calculating some values, storing them in variables (e.g., now above) and sending those variables to the template. Particularly lazy programmers would note that it’s slightly redundant to have to give names for temporary variables and give names for the template variables. Not only is it redundant; it’s extra typing.

So if you’re one of those lazy programmers and you like keeping code particularly concise, you can take advantage of a built-in Python function called locals(). locals() returns a dictionary of all variables defined within the local scope, along with their values. Thus, the above view could be rewritten like so:

def current_datetime(request):
    current_date = datetime.datetime.now()
    return render_to_response('current_datetime.html', locals())

Here, instead of manually specifying the context dictionary as before, we instead pass the value of locals(), which will include all variables defined at that point in the function’s execution. As a consequence, we’ve renamed the now variable to current_date, because that’s the variable name that the template expects. In this example, locals() doesn’t offer a huge improvement, but this technique can save you some typing if you’ve got several template variables to define — or if you’re lazy.

One thing to watch out for when using locals() is that it includes every local variable, which may comprise more variables than you actually want your template to have access to. In the above example, locals() will also include request. Whether this matters to you depends on your application.

A final thing to consider is that locals() incurs a small bit of overhead, because when you call it, Python has to create the dictionary dynamically. If you specify the context dictionary manually, you avoid this overhead.

Subdirectories in get_template()

It can get unwieldy to store all of your templates in a single directory. You might like to store templates in subdirectories of your template directory, and that’s fine. (In fact, we’d recommend it, and some more advanced Django features, such as the generic views system we’ll cover in Chapter 9, expect this template layout as a default convention.)

Accomplishing that is easy. In your calls to get_template(), just include the subdirectory name and a slash before the template name, like so:

t = get_template('dateapp/current_datetime.html')

Because render_to_response() is a small wrapper around get_template(), you can do the same thing with the first argument to render_to_response().

There’s no limit to the depth of your subdirectory tree. Feel free to use subdirectories of subdirectories of subdirectories.

Windows users, note: Make sure to use forward slashes rather than backslashes. get_template() assumes a Unix-style filename designation.

The include template tag

Now that we’ve covered the template loading mechanism, we can introduce a built-in template tag that takes advantage of it: {% include %}. This tag allows you to include the contents of another template. The argument to the tag should be the name of the template to include, and the template name can be either a variable or a hard-coded (quoted) string, in either single or double quotes.

These two examples include the contents of the template nav.html. The examples are equivalent and illustrate that either single or double quotes are allowed:

{% include 'nav.html' %}
{% include "nav.html" %}

This example includes the contents of the template includes/nav.html:

{% include 'includes/nav.html' %}

This example includes the contents of the template whose name is contained in the variable template_name:

{% include template_name %}

As in get_template(), the filename of the template is determined by adding the template directory from TEMPLATE_DIRS to the requested template name.

If an included template contains any template code — such as tags or variables — then it will get evaluated with the context of the template that’s including it.

If a template with the given name isn’t found, Django will do one of two things:

  • If your DEBUG setting is set to True, you’ll see the TemplateDoesNotExist exception on a Django error page.
  • If your DEBUG setting is set to False, the tag will fail silently, displaying nothing in the place of the tag.

Template inheritance

Our template examples so far have been tiny HTML snippets, but in the real world, you’ll be using Django’s template system to output entire HTML pages. This leads to a common Web development problem: Across a Web site, how does one reduce the duplication and redundancy of common page areas, such as sitewide navigation?

A classic way of solving this problem is to use server-side includes, directives you can embed within your HTML pages to “include” one Web page inside another. Indeed, Django supports that approach, with the {% include %} template tag we described above. But the preferred way of solving this problem with Django is to use a more elegant strategy called template inheritance.

In essence, template inheritance lets you build a base “skeleton” template that contains all the common parts of your site and defines “blocks” that child templates can override.

Let’s see an example of this by creating a more complete template for our current_datetime view, by editing the current_datetime.html file:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
<html lang="en">
<head>
    <title>The current time</title>
</head>
<body>
    <h1>My helpful timestamp site</h1>
    <p>It is now {{ current_date }}.</p>

    <hr>
    <p>Thanks for visiting my site.</p>
</body>
</html>

That looks just fine, but what happens when we want to create a template for another view — say, the hours_ahead view from Chapter 3? If we want again to make a nice, valid, full HTML template, we’d create something like:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
<html lang="en">
<head>
    <title>Future time</title>
</head>
<body>
    <h1>My helpful timestamp site</h1>
    <p>In {{ hour_offset }} hour(s), it will be {{ next_time }}.</p>

    <hr>
    <p>Thanks for visiting my site.</p>
</body>
</html>

Clearly, we’ve just duplicated a lot of HTML. Imagine if we had a few stylesheets included on every page, maybe a navigation bar, perhaps some JavaScript… We’d end up putting all sorts of redundant HTML into each template.

The server-side include solution to this problem would be to factor out the common bits in both templates and save them in separate template snippets, which would then be included in each template. Perhaps you’d store the top bit of the template in a file called header.html:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
<html lang="en">
<head>

And perhaps you’d store the bottom bit in a file called footer.html:

    <hr>
    <p>Thanks for visiting my site.</p>
</body>
</html>

With an include-based strategy, headers and footers are easy. But it’s the middle ground that’s messy. In this example, both pages feature a title — <h1>My helpful timestamp site</h1> — but that title can’t fit into header.html because the <title> on both pages is different. If we included the <h1> in the header, we’d have to include the <title>, which wouldn’t allow us to customize it per page. See where this is going?

Django’s template inheritance system solves these problems. You can think of it as an “inside out” version of server-side includes. Instead of defining the snippets that are common, you define the snippets that are different.

The first step is to define a base template — a skeleton of your page that child templates will later fill in. Here’s a base template for our ongoing example:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
<html lang="en">
<head>
    <title>{% block title %}{% endblock %}</title>
</head>
<body>
    <h1>My helpful timestamp site</h1>
    {% block content %}{% endblock %}
    {% block footer %}
    <hr>
    <p>Thanks for visiting my site.</p>
    {% endblock %}
</body>
</html>

This template, which we’ll call base.html, defines a simple HTML skeleton document that we’ll use for all the pages on the site. It’s the job of child templates to override, or add to, or leave alone the contents of the blocks. (If you’re following along at home, save this file to your template directory.)

We’re using a template tag here that you haven’t seen before — the {% block %} tag. All the {% block %} tags do is to tell the template engine that a child template may override those portions of the template.

Now that we’ve got this base template, we can modify our existing current_datetime.html template to use it:

{% extends "base.html" %}

{% block title %}The current time{% endblock %}

{% block content %}
<p>It is now {{ current_date }}.</p>
{% endblock %}

While we’re at it, let’s create a template for the hours_ahead view from Chapter 3. (If you’re following along with code, we’ll leave it up to you to change hours_ahead to use the template system.) Here’s what that would look like:

{% extends "base.html" %}

{% block title %}Future time{% endblock %}

{% block content %}
<p>In {{ hour_offset }} hour(s), it will be {{ next_time }}.</p>
{% endblock %}

Isn’t this beautiful? Each template contains only the code that’s unique to that template. No redundancy needed. If you need to make a sitewide design change, just make the change to base.html, and all of the other templates will immediately reflect the change.

Here’s how it works:

  • When you load the template current_datetime.html, the template engine sees the {% extends %} tag, noting that this template is a child template. The engine immediately loads the parent template — in this case, base.html.

  • At that point, the template engine notices the three {% block %} tags in base.html and replaces those blocks with the contents of the child template. So, the title we’ve defined in {% block title %} will be used, as will the {% block content %}.

    Note that since the child template doesn’t define the footer block, the template system uses the value from the parent template instead. Content within a {% block %} tag in a parent template is always used as a fallback.

You can use as many levels of inheritance as needed. One common way of using inheritance is the following three-level approach:

  • Create a base.html template that holds the main look-and-feel of your site. This is the stuff that rarely, if ever, changes.
  • Create a base_SECTION.html template for each “section” of your site. For example, base_photos.html, base_forum.html. These templates all extend base.html and include section-specific styles/design.
  • Create individual templates for each type of page, such as a forum page or a photo gallery. These templates extend the appropriate section template.

This approach maximizes code reuse and makes it easy to add items to shared areas, such as section-wide navigation.

Here are some tips for working with template inheritance:

  • If you use {% extends %} in a template, it must be the first template tag in that template. Otherwise, template inheritance won’t work.
  • Generally, the more {% block %} tags in your base templates, the better. Remember, child templates don’t have to define all parent blocks, so you can fill in reasonable defaults in a number of blocks, then only define the ones you need in the child templates. It’s better to have more hooks than fewer hooks.
  • If you find yourself duplicating code in a number of templates, it probably means you should move that code to a {% block %} in a parent template.
  • If you need to get the content of the block from the parent template, the {{ block.super }} variable will do the trick. This is useful if you want to add to the contents of a parent block instead of completely overriding it.
  • You may not define multiple {% block %} tags with the same name in the same template. This limitation exists because a block tag works in “both” directions. That is, a block tag doesn’t just provide a hole to fill — it also defines the content that fills the hole in the parent. If there were two similarly-named {% block %} tags in a template, that template’s parent wouldn’t know which one of the blocks’ content to use.
  • The template name you pass to {% extends %} is loaded using the same method that get_template() uses. That is, the template name is appended to your TEMPLATE_DIRS setting.
  • In most cases, the argument to {% extends %} will be a string, but it can also be a variable, if you don’t know the name of the parent template until runtime. This lets you do some cool, dynamic stuff.

Exercises

Here are a few exercises that will solidify some of the things you learned in this chapter. (Hint: Even if you think you understood everything, at least give these exercises, and their respective answers, a read. We introduce a couple of new tricks here.)

  1. You’ve got a list of musicians and the genre of music each one plays. This data is stored as a list of dictionaries, which will be hard-coded in your view module. (Usually we’d use a database for this, but we haven’t yet covered Django’s database layer.) The list looks like this:

    MUSICIANS = [
        {'name': 'Django Reinhardt', 'genre': 'jazz'},
        {'name': 'Jimi Hendrix',     'genre': 'rock'},
        {'name': 'Louis Armstrong',  'genre': 'jazz'},
        {'name': 'Pete Townsend',    'genre': 'rock'},
        {'name': 'Yanni',            'genre': 'new age'},
        {'name': 'Ella Fitzgerald',  'genre': 'jazz'},
        {'name': 'Wesley Willis',    'genre': 'casio'},
        {'name': 'John Lennon',      'genre': 'rock'},
        {'name': 'Bono',             'genre': 'rock'},
        {'name': 'Garth Brooks',     'genre': 'country'},
        {'name': 'Duke Ellington',   'genre': 'jazz'},
        {'name': 'William Shatner',  'genre': 'spoken word'},
        {'name': 'Madonna',          'genre': 'pop'},
    ]
    

    Write a Django view and corresponding template(s) that display an HTML <table> with a row for each musician in this list, in order. Each row should have two columns: the musician’s name and the type of music he/she plays.

  2. Once you’ve done that: For all the musicians who play jazz or rock — but not the others — bold their names by applying a style="font-weight: bold;" to their <td> cells.

  3. Once you’ve done that: For all the musicians who have a one-word name — but not the others — display an asterisk after their name. Add a footnote to the page that says “* Pretentious.” Maintain the style="font-weight bold;" from the previous exercise.

  4. Given the following three templates, devise a template-inheritance scheme that removes as much redundancy as possible.

    Template 1:

    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
    <html lang="en">
    <head>
        <link rel="stylesheet" href="default.css" type="text/css">
        <title>My to-do list</title>
    </head>
    <body>
        <h1 id="top">Latest tasks</h1>
        {% if task_list %}
            <ul>
            {% for task in task_list %}<li>{{ task }}</li>{% endfor %}
            </ul>
        {% else %}
            <p>You have no tasks.</p>
        {% endif %}
        <hr>
        <p><a href="#top">Back to top</a>.</p>
    </body>
    </html>
    

    Template 2:

    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
    <html lang="en">
    <head>
        <title>Task: {{ task.title }} | To-do list</title>
        <link rel="stylesheet" href="default.css" type="text/css">
    </head>
    <body>
        <h1 id="top">{{ task.title }}</h1>
        <p>{{ task.description }}</p>
        <hr>
        <p><a href="#top">Back to top</a>.</p>
    </body>
    </html>
    

    Template 3:

    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
    <html lang="en">
    <head>
        <title>Completed tasks | To-do list</title>
        <link rel="stylesheet" href="default.css" type="text/css">
        <script type="text/javascript" src="completed.js">
    </head>
    <body>
        <h1 id="top">{{ task.title }}</h1>
        <p>{{ task.description }}</p>
        <hr>
        <p><a href="#top">Back to top</a>.</p>
    </body>
    </html>
    

Answers to exercises

  1. Here’s one possible implementation of the view:

    from django.shortcuts import render_to_response
    
    MUSICIANS = [
        # ...
    ]
    
    def musician_list(request):
        return render_to_response('musician_list.html', {'musicians': MUSICIANS})
    

    And here’s the template:

    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
    <html lang="en">
    <head>
        <title>Musician list</title>
    </head>
    <body>
        <table>
        <tr><th>Musician</th><th>Genre</th></tr>
        {% for musician in musicians %}
            <tr>
            <td>{{ musician.name }}</td>
            <td>{{ musician.genre }}</td>
            </tr>
        {% endfor %}
        </table>
    </body>
    </html>
    
  2. A rather clumsy way to do this would be to use {% ifequal %} in the template. The view would stay the same as in the answer to the last exercise, and the template’s {% for %} loop would change to something like this:

    {% for musician in musicians %}
        <tr>
        <td {% ifequal musician.genre 'jazz' %}style="font-weight: bold;"{% endifequal %}
            {% ifequal musician.genre 'rock' %}style="font-weight: bold;"{% endifequal %}>
          {{ musician.name }}
        </td>
        <td>{{ musician.genre }}</td>
        </tr>
    {% endfor %}
    

    This is overly verbose, repetitive and error-prone — and it illustrates an important point. A key to mastering Django’s template system is to know what to pass to the template. Because templates do not have the full programming-language power of an environment you might be used to, it’s more important in a Django environment to do as much business logic (as opposed to presentation logic) as possible in your view.

    In this case, a cleaner way of solving the problem would be to have the view precalculate whether a musician’s name is bolded. After all, this is business logic, not presentation logic. The presentation logic dictates how the special-case genres should be displayed, not which genres are special-cased. This is an important distinction.

    Here’s one way to code the view:

    def musician_list(request):
        musicians = []
        for m in MUSICIANS:
            musicians.append({
                'name': m['name'],
                'genre': m['genre'],
                'is_important': m['genre'] in ('rock', 'jazz'),
            })
        return render_to_response('musician_list.html', {'musicians': musicians})
    

    And with that view, you could use this template code:

    {% for musician in musicians %}
        <tr>
        <td{% if musician.is_important %} style="font-weight: bold;"{% endif %}>
          {{ musician.name }}
        </td>
        <td>{{ musician.genre }}</td>
        </tr>
    {% endfor %}
    

    See how much cleaner that is in the template? Even this is more complex than it usually will be, because usually you’ll be dealing with database objects, and database objects can have custom methods (such as is_important()). We’ll cover database objects in the next chapter.

  3. This is a similar problem to the previous exercise, and the solution is also similar. The key is to precalculate whether a musician deserves an asterisk next to his or her name. Because that’s business logic, it belongs in the view.

    Here’s one possible view implementation:

    def musician_list(request):
        musicians = []
        for m in MUSICIANS:
            musicians.append({
                'name': m['name'],
                'genre': m['genre'],
                'is_important': m['genre'] in ('rock', 'jazz'),
                'is_pretentious': ' ' not in m['name'],
            })
        return render_to_response('musician_list.html', {'musicians': musicians})
    

    We’re using the expression ' ' not in m['name'], which returns True if m['name'] doesn’t include a space. You could also use the .find() method, like this:

    'is_pretentious': m['name'].find(' ') == -1
    

    Note that we’re calling this variable is_pretentious rather than has_asterisk, because the fact that we’re using asterisks is a presentation decision.

    With the above view, you could use this template code:

    {% for musician in musicians %}
        <tr>
        <td{% if musician.is_important %} style="font-weight: bold;"{% endif %}>
          {% if musician.is_pretentious %}* {% endif %}{{ musician.name }}
        </td>
        <td>{{ musician.genre }}</td>
        </tr>
    {% endfor %}
    

    Don’t forget the “* Pretentious.” at the bottom of the template.

    For bonus points, be a perfectionist and only display the “* Pretentious” footnote if there’s at least one pretentious musician. Determine the presence of a pretentious musician in the view, like so:

    def musician_list(request):
        musicians = []
        has_pretentious = False
        for m in MUSICIANS:
            if ' ' not in m['name']:
                has_pretentious = True
            musicians.append({
                'name': m['name'],
                'genre': m['genre'],
                'is_important': m['genre'] in ('rock', 'jazz'),
                'is_pretentious': ' ' not in m['name'],
            })
        return render_to_response('musician_list.html', {
            'musicians': musicians,
            'has_pretentious': has_pretentious,
        })
    

    In this implementation, we pass an extra template variable, has_pretentious, to the template. Then use it in the template like so:

    {% if has_pretentious %}* Pretentious{% endif %}
    
  4. Here’s one way to write the base template:

    <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
        <html lang="en">
        <head>
            <link rel="stylesheet" href="default.css" type="text/css">
            <title>{% block title %}{% endblock %}</title>
            {% block extrahead %}{% endblock %}
        </head>
        <body>
            <h1 id="top">{% block headline %}{% endblock %}</h1>
            {% block content %}{% endblock %}
            <hr>
            <p><a href="#top">Back to top</a>.</p>
        </body>
        </html>
    

    And, with that base template, here’s how the child templates might look.

    Template 1:

    {% extends "base.html" %}
    
    {% block title %}My to-do list{% endblock %}
    
    {% block headline %}Latest tasks{% endblock %}
    
    {% block content %}
    {% if task_list %}
        <ul>
        {% for task in task_list %}<li>{{ task }}</li>{% endfor %}
        </ul>
    {% else %}
        <p>You have no tasks.</p>
    {% endif %}
    {% endblock %}
    

    Template 2:

    {% extends "base.html" %}
    
    {% block title %}Task: {{ task.title }} | To-do list{% endblock %}
    
    {% block headline %}{{ task.title }}{% endblock %}
    
    {% block content %}<p>{{ task.description }}</p>{% endblock %}
    

    Template 3:

    {% extends "base.html" %}
    
    {% block title %}Completed tasks | To-do list{% endblock %}
    
    {% block extrahead %}<script type="text/javascript" src="completed.js">{% endblock %}
    
    {% block headline %}{{ task.title }}{% endblock %}
    
    {% block content %}<p>{{ task.description }}</p>{% endblock %}
    

    Note that we like to put an empty line of space between {% block %} sections, but that’s just our personal style. Any text outside of {% block %} tags in child templates will not be rendered.

Copyright 2006 Adrian Holovaty and Jacob Kaplan-Moss.
This work is licensed under the GNU Free Document License.
Hosting graciously provided by media temple
Comments X

Comments are closed on this chapter.

We're no longer accepting comments on this version of this chapter.

Many thanks to all those who commented.

      About this comment system

      This site is using a contextual comment system to help us gather targeted feedback about the book. Instead of commenting on an entire chapter, you can leave comments on any indivdual "block" in the chapter. A "block" with comments looks like this:

      A "block" is a paragraph, list item, code sample, or other small chunk of content. It'll get highlighted when you select it:

      To post a comment on a block, just click in the gutter next to the bit you want to comment on:

      As we edit the book, we'll review everyone's comments and roll them into a future version of the book. We'll mark reviewed comments with a little checkmark:

      Please make sure to leave a full name (and not a nickname or screenname) if you'd like your contributions acknowledged in print.

      Many, many thanks to Jack Slocum; the inspiration and much of the code for the comment system comes from Jack's blog, and this site couldn't have been built without his wonderful YAHOO.ext library. Thanks also to Yahoo for YUI itself.