Django Template Tags and Filters

As I’ve mentioned already, the template system ships with built-in tags and filters. The sections that follow provide a rundown of the most common tags and filters.

Tags

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 may also take one or several {% elif %} clauses as well:

{% if athlete_list %}
Number of athletes: {{ athlete_list|length }}
{% elif athlete_in_locker_room_list %}
<p>Athletes should be out of the locker room soon! </p>
{% elif ...
...
{% else %}
<p>No athletes. </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 %}
<p>Both athletes and coaches are available. </p>
{% endif %}

{% if not athlete_list %}
<p>There are no athletes. </p>
{% endif %}

{% if athlete_list or coach_list %}
<p>There are some athletes or some coaches. </p>
{% endif %}

{% if not athlete_list or coach_list %}
<p>There are no athletes or there are some coaches. </p>
{% endif %}

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

Use of both and and or clauses within the same tag is allowed, with and having higher precedence than or e.g.:

{% if athlete_list and coach_list or cheerleader_list %}

will be interpreted like:

if (athlete_list and coach_list) or cheerleader_list

NOTE: Use of actual parentheses in the if tag is invalid syntax.

If you need parentheses to indicate precedence, you should use nested if tags. The use of parentheses for controlling order of operations is not supported. If you find yourself needing parentheses, consider performing logic outside the template and passing the result of that as a dedicated template variable. Or, just use nested {% if %} tags, like this:

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

Multiple uses of the same logical operator are fine, but you can’t combine different operators. For example, this is valid:

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

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, you could use the following 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 athlete in athlete_list %}
<h1>{{ athlete.name }}</h1>
<ul>
    {% for sport in athlete.sports_played %}
    <li>{{ sport }}</li>
    {% endfor %}
</ul>
{% endfor %}

If you need to loop over a list of lists, you can unpack the values in each sub list into individual variables.

For example, if your context contains a list of (x,y) coordinates called points, you could use the following to output the list of points:

{% for x, y in points %}
<p>There is a point at {{ x }},{{ y }}</p>
{% endfor %}

This can also be useful if you need to access the items in a dictionary. For example, if your context contained a dictionary data, the following would display the keys and values of the dictionary:

{% for key, value in data.items %}
{{ key }}: {{ value }}
{% endfor %}

A common pattern is to check the size of the list before looping over it, and outputting some special text if the list is empty:

{% if athlete_list %}

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

{% else %}
<p>There are no athletes. Only computer programmers.</p>
{% endif %}

Because this pattern is so common, the for tag supports an optional {% empty %} clause that lets you define what to output if the list is empty. This example is equivalent to the previous one:

{% for athlete in athlete_list %}
<p>{{ athlete.name }}</p>
{% empty %}
<p>There are no athletes. Only computer programmers.</p>
{% 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 includes only 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 the section “Philosophies and Limitations”
later in this chapter for the reasoning behind this design decision.)

Within each {% for %} loop, you get access to a template variable called forloop. 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. Here’s an 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 1. 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. A common use for this is to put pipe characters between a list of links:
      {% for link in links %}
      {{ link }}{% if not forloop.last %} | {% endif %}
      {% endfor %}
    

    The above template code might output something like this:

      Link1 | Link2 | Link3 | Link4
    

    Another common use for this is to put a comma between words in a list:

      <p>Favorite places:</p>
          {% for p in places %}{{ p }}{% if not forloop.last %}, {% endif %}
          {% endfor %}
    
  • forloop.parentloop is a reference to the forloop object for the parent loop, in case of nested loops. Here’s an 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 forloop variable is only available within loops. After the template parser has reached {% endfor %}, forloop disappears.

ifequal/ifnotequal

The Django template system is not a full-fledged programming language and thus does not allow you to execute arbitrary Python statements. (More on this idea in the section “Philosophies and Limitations”).

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’t 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 %}.

An alternative to the ifequal tag is to use the if tag and the “==” operator.

The {% ifnotequal %} tag is identical to the ifequal tag, except that it tests whether the two arguments are not equal. An alternative to the ifnotequal tag is to use the if tag and the “!=” operator.

Comments

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

{# This is a comment #}

The comment will not be output when the template is rendered. Comments using this syntax cannot span multiple lines. This limitation improves template parsing performance.

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 {# this is not
a comment #}
test.

If you want to use multi-line comments, use the {% comment %} template tag, like this:

{% comment %}
This is a
multi-line comment.
{% endcomment %}

Comment tags cannot be nested.

Filters

As explained earlier in this chapter, template filters are simple ways of altering the value of variables before they’re displayed. Filters use a pipe character, like this:

{{ name|lower }}

This displays the value of the {{ name }} variable after being filtered through the lower filter, which converts text to lowercase. Filters can be chained – that is, they can be used in tandem such that the output of one filter is applied to the next.

Here’s an example that takes the first element in a list and converts it to uppercase:

{{ my_list|first|upper }}

Some filters take arguments. A filter argument comes after a colon and is always in double quotes. For example:

{{ bio|truncatewords:"30" }}

This displays the first 30 words of the bio variable.

The following are a few of the most important filters. Appendix E covers the rest.

  • addslashes: Adds a backslash before any backslash, single quote, or double quote. This is useful for escaping strings. For example:{{ value|addslashes }}
  • 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 E.
  • length: Returns the length of the value. For a list, this returns the number of elements. For a string, this returns the number of characters. If the variable is undefined, length returns 0.

Philosophies and Limitations

Now that you’ve gotten a feel for the Django Template Language(DTL), it is probably time to explain the basic design philosophy behind the DTL. First and foremost, the limitations to the DTL are intentional.

Django was developed in the high volume, ever-changing environment of an online newsroom. The original creators of Django had a very definite set of philosophies in creating the DTL.

These philosophies remain core to Django today. They are:

  1. Separate logic from presentation
  2. Discourage redundancy
  3. Be decoupled from HTML
  4. XML is bad
  5. Assume designer competence
  6. Treat whitespace obviously
  7. Don’t invent a programming language
  8. Ensure safety and security
  9. Extensible

1. Separate logic from presentation

A template system is 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.

2. Discourage redundancy

The majority of dynamic Web sites use some sort of common site-wide design – a common header, footer, navigation bar, etc. The Django template system should make it easy to store those elements in a single place, eliminating duplicate code. This is the philosophy behind template inheritance.

3. Be decoupled from HTML

The template system shouldn’t be designed so that it only outputs HTML. It should be equally good at generating other text-based formats, or just plain text.

4. XML should not be used for template languages

Using an XML engine to parse templates introduces a whole new world of human error in editing templates – and incurs an unacceptable level of overhead in template processing.

5. Assume designer competence

The template system shouldn’t be 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.

6. Treat whitespace obviously

The template system shouldn’t do magic things with whitespace. If a template includes whitespace, the system should treat the whitespace as it treats text – just display it. Any whitespace that’s not in a template tag should be displayed.

7. Don’t invent a programming language

The template system intentionally doesn’t allow the following:

  • Assignment to variables
  • Advanced logic

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.

The Django template system recognizes that templates are most often written by designers, not programmers, and therefore should not assume Python knowledge.

8. Safety and security

The template system, out of the box, should forbid the inclusion of malicious code – such as commands that delete database records. This is another reason the template system doesn’t allow arbitrary Python code.

9. Extensibility

The template system should recognize that advanced template authors may want to extend its technology. This is the philosophy behind custom template tags and filters.

~~

Having worked with many different templating systems myself over the years, I whole-heartedly endorse this approach – the DTL and the way it has been designed is one of the major pluses of the Django framework.

When the pressure is on to Get Stuff Done, and you have both designers and programmers trying to communicate and get all the of the last minute tasks done, Django just gets out of the way and lets each team concentrate on what they are good at.

Once you have found this out for yourself through real-life practice, you will find out very quickly why Django really is the “framework for perfectionists with deadlines”.

With all this in mind, Django is flexible – it does not require you to use the DTL. More than any other component of Web applications, template syntax is highly subjective, and programmers’ opinions vary wildly. The fact that Python alone has dozens, if not hundreds, of open source template-language implementations supports this point. Each was likely created because its developer deemed all existing template languages inadequate.

Because Django is intended to be a full-stack Web framework that provides all the pieces necessary for Web developers to be productive, most times it’s more convenient to use the DTL, but it’s not a strict requirement in any sense.