Django Templates

This post is part of my Django series. You can see an overview of the series along with instruction on how to get all the source code here.

This article assumes you are comfortable creating a Django project, can create apps and register them into the INSTALLED_APPS list of the settings file. If not please read my Django HelloWorld article.

This article assumes you have a project called DjangoSandBox in an application called templatesintroduction.


for endfor

We can loop through a collection with the for and endfor tags

{%  for colour in colours %}
    {{  colour }}
{% endfor %}

Where each element in a collection is a tuple we can unpack the tuple elements into loop variables.

If out context data is:

'lettersandordinals': [(1, 'a'), (2, 'b'), (3, 'c')],

We could loop like this:

{% for number, letter in lettersandordinals %}
    {{ number }} ({{ letter  }})
{% endfor %}

When we are looping through a dictionary we can access the key and value.

If our context data is:

'worldcities': {'UK': ['Plymouth', 'Bristol'], 'France': ['Grenoble']},

We could loop like this:

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

for empty

We can use the empty tag as a conditional check when looping through an empty collection.

{% for empty in emptycollection %}
    The collection is not empty
    {%  empty %}
    This collection is empty!!!
{% endfor %}

Loop Variables

When within a loop we can access various loop variables from the forloop template variable;

Variable Description
forloop.counter The current iteration count where 1 is the first count
forloop.counter0 The current iteration count where 0 is the first count
forloop.revcounter The number of iterations from the end of the loop; where 1 is the end
forloop.revcounter0 The number of iterations from the end of the loop; where 0 is the end
forloop.first Returns true if this is the first iteration
forloop.last Returns true if this is the last iteration
forloop.parentloop Allows access to a parent loop variable if we are a nested loop
    {% for number in numbers  %}
            {{ number }}
            {{ forloop.counter }}
            {{ forloop.counter0 }}
            {{ forloop.revcounter }}
            {{ forloop.revcounter0 }}
            {{ forloop.first }}
            {{ forloop.last }}
    {% endfor %}


We can iterate through a collection backwards with the reversed tag

{% for number in numbers reversed %}
    {{ number }}
{% endfor %}


The cycle flag allow us to identify iterations. We can define 2 or more strings, numbers or variables along with the cycle tag. Each iteration will iterate through all the elements passed in. Here we assign the output to the oddeven variable which we can access later on.

{% for colour in colours %}
    {{  colour }}
    {% cycle 'odd' 'even' as oddeven%}
    {{ oddeven }}
{% endfor %}


We can determine when a variable has changed between iterations with the ifchanged tag.

If out context data was:

 'duplicatednumbers': [1, 1, 1, 1, 2, 2, 2, 3, 3, 3, 3],
    {% for number in duplicatednumbers %}
            {{ number }}
            {% ifchanged number %} {#  can take multiple arguments #}
            {% else %}
                (not changed)
            {% endifchanged %}
    {% endfor %}

Would output:

1 (changed)
1 (not changed)
1 (not changed)
1 (not changed)
2 (changed)
2 (not changed)
2 (not changed)
3 (changed)
3 (not changed)
3 (not changed)
3 (not changed)


We can generate html for a list using UL elements with the unordered_list tag

{{ numbers|unordered_list }}

This would generate something like this:


If the collection is a dictionary of lists we generate nested lists.



Where we have a list of dictionaries; we can sort by a common child column.

If our context data was:

boysandgirls': [
                   {'name': 'Lukelad', 'age': 26, 'sex': 'male'},
                   {'name': 'Lukette', 'age': 66, 'sex': 'female'},
                   {'name': 'Luke', 'age': 36, 'sex': 'male'},
                   {'name': 'Lukecy', 'age': 36, 'sex': 'female'}]}

We can sort by the age column ascending by :

{{ boysandgirls|dictsort:"age" }}

Would output:

[{'name': 'Lukelad', 'age': 26, 'sex': 'male'}, {'name': 'Luke', 'age': 36, 'sex': 'male'}, {'name': 'Lukecy', 'age': 36, 'sex': 'female'}, {'name': 'Lukette', 'age': 66, 'sex': 'female'}]


We can also sort in a reversed order with dictsortreversed pipe:

{{ boysandgirls|dictsortreversed:"age" }}

Would output:

[{'name': 'Lukette', 'age': 66, 'sex': 'female'}, {'name': 'Luke', 'age': 36, 'sex': 'male'}, {'name': 'Lukecy', 'age': 36, 'sex': 'female'}, {'name': 'Lukelad', 'age': 26, 'sex': 'male'}]


Continuing with our list of people as a dictionary we can group upon a common field to produce a collection for each grouped set. Here we group by the sex column to produce a collection of boys and girls.

We have access to the group value via the grouper variable upon each group.

If our context data was:

boysandgirls': [
                   {'name': 'Lukelad', 'age': 26, 'sex': 'male'},
                   {'name': 'Lukette', 'age': 66, 'sex': 'female'},
                   {'name': 'Luke', 'age': 36, 'sex': 'male'},
                   {'name': 'Lukecy', 'age': 36, 'sex': 'female'}]}

We can group by sex and loop through the groups and then elements in each group:

{% regroup boysandgirls|dictsort:"sex" by sex as boys_and_girls_grouped  %}
{#The grouping does not sort the data. | through dictsort if required#}
{% for sex_group in boys_and_girls_grouped %}
    <li>{{ sex_group.grouper | capfirst }}
        {% for item in sex_group.list %}
          <li>{{ }}: {{ item.age }}</li>
        {% endfor %}
{% endfor %}

The following would be outputted:

    Lukette: 66
    Lukecy: 36
    Lukelad: 26
    Luke: 36


We can join all elements in collection as a string with the join filter along with the separator character.

{{ numbers|join:":" }}

If our context data was:

numbers is: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],

Would output:


first and last

The first and last pipes can be used to extract the first and last element of a collection.

First of {{ numbers }} is {{ numbers|first }}
Last of {{ numbers }} is {{ numbers|last }}

length and length_is

The length filter returns the number of elements in a collection while the length_is tag returns true or false depending upon if our collection size is equal to our comparison argument.

{{ numbers }} has {{ numbers|length }} elements
Is {{ numbers }} 10 in length? {{ numbers|length_is:"10" }}

If out context data was:

  'numbers': [1, 2, 3, 4, 5, 6, 7, 8, 9, 10],

Would output:

[1, 2, 3, 4, 5, 6, 7, 8, 9, 10] has 10 elements
/ Is [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] 10 in length? True


The make_list can be used to create a list from a literal. 123456789 would become a list of the numbers 0 – 9.

123456789 becomes {{ 123456789|make_list  }}
Hello becomes {{ "Hello"|make_list  }}

Would output:

123456789 becomes ['1', '2', '3', '4', '5', '6', '7', '8', '9']
Hello becomes ['H', 'e', 'l', 'l', 'o']


We can extract a random element from a collection with the random pipe.

A random of {{ numbers }} is {{ numbers|random }}

Collection Slicing ### {#Collection Slicing}

We can use the python collection slicing functionality to slice elements from a collection.

<br />The first two of {{ numbers }} is {{ numbers|slice:":2"}}
The last two of {{ numbers }} is {{ numbers|slice:"-2:"}}
Elements 5 to 8 {{ numbers }} is {{ numbers|slice:"4:8"}}

Would output

The first two of [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] is [1, 2]
The last two of [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] is [9, 10]
Elements 5 to 8 [1, 2, 3, 4, 5, 6, 7, 8, 9, 10] is [5, 6, 7, 8]


if, elif, else and endif

Django templates supports if, elif, else and endif tags for if conditions.

    {% if age < 50 %}
        Less than 50
    {% elif age == 50 %}
        Equal to 50
    {% else  %}
        The else clause
    {% endif %}

or & and

We can compound conditions with the ‘or’ and ‘and’ operators.

    {% if age < 50 and age > 10 %}
        Less than 50 but more than 10
    {% elif age == 50 or age == 51 %}
        Equal to 50
    {% else  %}
        The else clause
    {% endif %}


Boolean negation can be made with the not operator.

    {%  if not false %}
        Not False
    {% endif %}


We can check for the inclusion of an element within a collection with the in operator.

    {%  if 1 in numbers%}
        1 is in {{  numbers }}
    {% endif %}

not in

We can negate the in operator with the not operator to check for the non inclusion of an element within a collection.

    {%  if 11 not in numbers%}
        11 is not in {{  numbers }}
    {% endif %}


The ifequal, else endifequal tags are a shortcut for if x = y, else and endif.

    {% ifequal valueone valueone%}
        {{ valueone }} and {{ valueone }} are equal
    {% else %}
        {{ valueone }} and {{ valueone }} are not equal
    {% endifequal %}

ifequal, endifequal, ifnotequal and endifnotequal

Negation of ifequal/endifequal is made with the ifnotequal and endifnotequal operators.

    {% ifnotequal valueone valuetwo%}
        {{ valueone }} and {{ valuetwo }} are not equal
    {% else %}
        {{ valueone }} and {{ valuetwo }} are equal
    {% endifnotequal %}


The firstof tag outputs the first true (non zero) element; this can be used with boolean and integer values.

    {% firstof 0 0 0 1 "All were false"  %}
    {% firstof 0 0 0 0 "All were false"  %}


Filters allow piping results through multiple tags; here we filter the html string to be all lower case and then capitalised upon the first word:

    {% filter lower|capfirst %}This text will be HTML-escaped.{% endfilter %}

Filters can be used with other tags; here we check the length of a collection and pipe it to the >= comparison operator.

    {% if numbers|length >= 5 %}
        There are more than five numbers!
    {% endif %}


Variables can be declare with the with operator.

{% with count=10 %}
    There are {{ count }} item{{ 2|pluralize }}
{% endwith %}

Operator Precedence

Parenthesis is not supported within templates so you must rely on operator precedence

    The precedence is the same as python: or, and, not, in, ==, !=, <, >, <=, >=

Dates & Times


The now keyword can be used to get the current date and time.

It is: {% now "c"  %}

Formatting Dates

The “c” above is a date formatter specifying the ISO 8601 format; we can use any date time formatters as defined by Django date template formatters.

It is {% now "f A jS of F Y " %}

Outputs something similar to this:

It is 10:52 AM 12th of July 2015
  • f: Time, in 12-hour hours and minutes, with minutes left off if they’re zero. Proprietary extension.
  • A: ‘AM’ or ‘PM’.
  • j: Day of the month without leading zeros.
  • S: English ordinal suffix for day of the month, 2 characters.
  • o: SO-8601 week-numbering year, corresponding to the ISO-8601 week number (W)
  • f: Time, in 12-hour hours and minutes, with minutes left off if they’re zero. Proprietary extension.
  • F: Month, textual, long
  • Y: Year, 4 digits.

We also have access to various predefined formats which respect the local settings.

{% now "DATE_FORMAT" %}

Which outputs the following UK formatted dates.

July 12, 2015
July 12, 2015, 10:52 a.m.
07/12/2015 10:52 a.m.

date and time

We can use the date and time filter with a date time object along with formatting criteria. We can use all the date time formats as mentioned above.

{{ when|date:"D d M Y" }} 
{{ when|time:"H:i" }}

If our context data was:

when is

Would output:

Sun 12 Jul 2015 


We can use the the timesince filter to output the difference between two date times.

{{ yesterday }} since {{ when }} = {{ yesterday|timesince:when}}
{{ when }} until {{ tomorrow }} = {{ when|timesince:tomorrow}}

If our context was:

'yesterday': - timedelta(days=1)
'tomorrow': + timedelta(days=1)

Would output:

July 11, 2015, 10:52 a.m. since July 12, 2015, 10:52 a.m. = 23 hours, 59 minutes
July 12, 2015, 10:52 a.m. until July 13, 2015, 10:52 a.m. = 1 day



Comments are made with the comment tag. All code between the comment and endcomment tags are commented out. This includes template code.

{% comment "Optional note" %}
<p>This is a comment. Even template code is commented out {{ who }}</p>
{% endcomment %}


A wrapper around Python’s pretty print library; pprint.pprint(). Use as a filter against any template parameter.

{{ request|pprint }}


The debug tag can be used to output a whole bunch of debug information about Django; I would favour using the Django Debug Toolbar

{%  debug %}}

Block & Extends

The block and extends tags are used to reuse sections of templates and is similar to ASP.NET master pages.

Imagine we had static data and format between pages which we would like to reuse. We can extract this into a template page. For example the menus, placement of the title and the static files such as css files.

The parent page would declare the required sections with the block tag; for example the page title along with the actual physical content of the page.

Physical child pages would then extend the page using the extends tag and provide content for the defined block tag sections of the parent template.

Our parent template would be:

<!-- templates/templatesintroduction/base_template.html -->
<h1>Welcome to the site</h1>
{%  block title %}
{%  endblock %}

{% block content %}
{% endblock %}

Our child template would be:

{%  extends 'templatesintroduction/base_template.html' %}

{%  block title %}
    <h2>This is the title</h2>
{%  endblock %}

{% block content %}
    <h3>This is the content</h3>
{% endblock %}

Would output:

<h1>Welcome to the site</h1>
<h2>This is the title</h2>
<h3>This is the content</h3>


The include tag allows us to include a template into the current template; we have access to the context data of the parent page within the include page.

  • All context data from the parent page is passed into the included template
  • We can prevent passing in the context data with the ‘only’ option
  • We can explicitly pass additional context data into the template with the ‘with’ command and providing key value paired data
  • We can combine the only and with tags; only the additional context data is passed in

Imagine the following template which displays the msg template parameter along with a default message.

<!-- templatesintroduction/include_one.html -->
{{ msg|default:"There was no message!!!" }}

We can then include this template into another as follows:

{% include "templatesintroduction/include_one.html" %}
{% include "templatesintroduction/include_one.html" with msg="This was overridden!!!" %}
{% include "templatesintroduction/include_one.html" with wrongmsg="This was overridden!!!" only %}

If our context to the outer template had the following:

'msg': "This is a message of hello!!!"

Would output:

This is a message of hello!!!
This was overridden!!!
There was no message!!!
  • The first include passes all context data into the include template; the parent context data is rendered
  • The second include passes all context data into the include templates but also passes in additional context data which overrides the value in the parent page context data
  • The third include uses the only tag. The parent context data is not passed in as as such the default message was used

Note: it seems that using the only keyword requires at least one context data element passed in with the ‘with’ option.


SSI includes the contents of a file without using the template rendering system; i.e. the contents of the file are displayed as is.

You need to add a permission to the file as static data otherwise you will get a permission denied error.

{% ssi "foo.css" %}



The widthratio can work out the ratio of a width based upon a value and its maximum value; {% widthratio 50 100 100 %}. This would read what is the width of 50 if it’s maximum value is 100 and the maximum width we want is 100.

The width is: {% widthratio 50 100 100 %}

Would output:

The width is: 50

We can assign the value to a variable with the ‘as’ command to use later on.

{% widthratio 50 100 100 as width %}
The width is: {{ width }}

divisibleby ###{#divisibleby}

The divisibleby tag can be used to determine if a modulus division would return 0.

Is 21 dividable by 3? = {{ 21|divisibleby:"3" }}
Is 21 dividable by 5? = {{ 21|divisibleby:"5" }}

Would output:

Is 21 dividable by 3? = True
Is 21 dividable by 5? = False


The filesizeformat tag provides an easy way of outputting a number as a file size format with the units.

{{ 1|filesizeformat }}
{{ 1024|filesizeformat }}
{{ 1048576|filesizeformat }}
{{ 1073741824|filesizeformat }}

Would output the following:

1 byte
1.0 KB
1.0 MB
1.0 GB


The floatformat tag can be used to round a floating point number to a defined number of decimal places.

{{ 11.1111|floatformat:"3" }}
{{ 11.1111|floatformat:"0" }}

Would output:


If the number being rounded has less decimal places than it is being rounded to 0’s are placed upon the entity to make up the remaining decimal places. We can use a negative number to prevent these surplus digits.

{{ 11|floatformat:"3" }}
{{ 11.1111|floatformat:"-3" }}
{{ 11|floatformat:"-3" }}

Would output:



The get_digit tag takes an integer as an ordinal position and returns the associated digit at this position from another number. The ordinal position starts at element 1 on the right hand side of the number; the following would return 8.

{{ 123456789|get_digit:"2" }}


The phone2numeric tag can be used to convert telephone numbers containing letters to their numerical counter part.

{{ "+44 1752 HELLO"|phone2numeric }}

Would output:

+44 1752 43556


We can use the add tag to perform addition. This not only adds two numbers together but it can concatenate two strings as well as appending one collection onto the end of another. Where a string can be cast to a number a conversion is made.

2 + 1 = {{ 2|add:1 }}
'2' + '1' = {{ '2'|add:'1' }}
'a' + 'b' = {{ 'a'|add:'b' }}
{{ onetwo }} + {{ onetwo }} = {{ onetwo|add:onetwo }}

If the context data was:

'onetwo': [1, 2]

Would output:

2 + 1 = 3
'2' + '1' = 3
'a' + 'b' = ab
[1, 2] + [1, 2] = [1, 2, 1, 2]


{{ }}

The {{ }} tag is used to write a value to the response stream; i.e to render the value of a variable to the template. This can be use with literals in the page or context data passed in as template variables.

{{ who }}

lower, upper, capfirst, title

  • The lower filter will make all letters lower case
  • The upper filter will make all letters upper case
  • The capfirst will make all letters lower case except the very first letter which will be upper case
  • The title filter will make all letters lower case except the first letter of every word which will be upper case
{{ who | lower }}
{{ who | upper }}
{{ who | capfirst }}
{{ who | title }}


The addslashes tag can be used to escape any quotes in a string with a backslash.

{{ "Simon's slashes"|addslashes }}

This would output:

Simon's slashes

ljust, center, rjust

We can space pad strings to be be a minimum of x characters. Where the string does not contain this number of characters spaces are used to build up the string to this size.

  • ljust: the string is aligned left by placing spaces on the right had side
  • center: the string is aligned in the centre by placing spaces evenly on each side
  • rjust: the string is aligned right by placing spaces on the left hand side
{{ "Hello"|ljust:20 }}
{{ "Hello"|center:20 }}
{{ "Hello"|rjust:20 }}


The cut command can be used to remove all instances of a string from another string. Here we remove the string el from the word Hello to leave Hlo.

{{ "Hello"|cut:"el" }}

default, default_if_none

The default filter allows us to provide a default value.

{{ NotProvided|default:"Nothing was provided here!!!" }}
{{ False|default:"The value was false!" }}
{{ None|default_if_none:"Only displayed if this is None" }}

If our context data was:

'False': False,
'None': None,

Would output:

Nothing was provided here!!!
The value was false!
Only displayed if this is None
  • The first call simply did not provided the template variable; the default message was outputted
  • The second uses the False template variable which equates to a False boolean. Any expression equating to false triggers the default value
  • The third call has a template variable but it is set to None. In this case we can trigger the default message by using the default_if_none filer.


We can use the pluralize filter to change a word to its pluralised variation. For example the word ‘example’ would become ‘examples’. The default appends the character s onto the word.

The filter works with a collection or an integer. Where a collection is used if it contains two or more elements the plural will be used. Where an integer is used if it is 2 or more the plural will be used.

Where a pluralized word differs from simply adding an ‘s’ character we can provide the single and plural endings. Here we append our static prefix of ‘pe’ with ‘rson’ or ‘ople’ to make person or people depending if we have 0/1 or 2 or more entries.

The plural of colour is colour{{ colours|pluralize }}.
The plural of colour is colour{{ 2 | pluralize }}.
The plural of person is pe{{ 2|pluralize:"rson,ople"}}.

If our context data was:

'colours': {'Red', 'Blue', 'Green'},

Would output:

The plural of colour is colours.
The plural of colour is colours.
The plural of person is people.

truncatechars, truncatechars_html, truncatewords and truncatewords_html

We can truncate strings with the truncatechars, truncatechars_html, truncatewords and truncatewords_html filters.

  • truncatechars truncates a string to be the first x characters
  • truncatechars_html truncates a string to be the first x characters. Where HTML tags are removed it ensures that any open tags are completed.
  • truncatewords truncates a string to be the first x characters. It will allow any words to be completed even if it means the final string is longer than the required x characters
  • truncatewords_html truncates a string to be the first x characters. It will allow words and HTML tags to be completed.
{{ longmessage|truncatechars:20 }}
{{ longboldmessage|truncatechars_html:20 }}
{{ longmessage|truncatewords:3 }}
{{ longboldmessage|truncatewords_html:3 }}


The spaceless tag can be used to “compress” html such that it contains no white space. White space which is displayed to the user is not affected.

{% spaceless %}
            This is some text
{% endspaceless %}

Would output:

<span><b><i>This is some text</i></b></span>


The template tag allows us to output template revered symbols such as {{ and {%.

{% templatetag openblock %} {% templatetag closeblock %}
{% templatetag openvariable %} {% templatetag closevariable %}
{% templatetag openbrace%} {% templatetag closebrace %}
{% templatetag opencomment %} {% templatetag closecomment %}

Would output:

{% %}
{{ }}
{ }
{# #}


The verbatim tag takes any characters between the verbatim and endverbatim and renders them into the HTML directly, regardless of what they contain.

{% verbatim %}
    {{this will output}}{%  as it nothing has happened %}
{% endverbatim %}

Would output:

{{this will output}}{%  as it nothing has happened %}

linebreaks and linebreaksbr

We can render newline characters in strings with the
html tag with the linebreaks and linebreaksbr filters. The difference is that linebreaks will append

tags around each line.

{{ messagewithnewlines|linebreaks }}
{{ messagewithnewlines|linebreaksbr }}


We can apply word wrap to x characters with the wordwrap tag

{{ longmessage|wordwrap:5 }}


We can output line number counts against each line with the linenumbers filter.

{{ messagewithnewlines|linenumbers|linebreaks }}

If the context data was:

'messagewithnewlines': "Thisncontainsnnewlines"

Would output:

1. This
2. contains
3. newlines


We can use the wordcount filter to provide a count of the number of words in a string.

There are {{ longmessage|wordcount }} words in "{{ longmessage }}"


We have access to all the normal Python string formatting options:

{{ 1234.567|stringformat:"E" }}
{{ 1234.567|stringformat:"F" }}
{{ 1234.567|stringformat:"d" }}

Would output:



We can convert boolean values to custom strings with the yesno filter. An optional value for None can be provided. If no None value is provided the False value is used.

{{ False|yesno:"yeah,no,maybe" }}
{{ True|yesno:"yeah,no,maybe" }}
{{ None|yesno:"yeah,no,maybe" }}
{{ None|yesno:"yeah,no" }}

Would output:



We can render a URL by referencing it via it’s name and namespace as defined within the URL routing config.

<a href="{% url 'templatesintroduction:urls'%}">URLs</a>

We can pass in any URL arguments in the order they are found in the view function:

<a href="{% url 'templatesintroduction:urls' 1 2%}">URLs</a>

We can name the arguments to match the view function arguments to by pass the order.

<a href="{% url 'templatesintroduction:urls' two=2 one=1%}">URLs</a>

Static Files

Any static files such as css or js files need to be registered within the settings file of the project:
STATIC_URL = '/static/'

On each template requiring a static file we need to load the static files:

{% load staticfiles %}

We can then include static files on our page:

<link rel="stylesheet" type="text/css" href="{% static 'css/styles.css' %}"/>
<img src="{% static 'imgs/tintin.jpg' %}" alt="My image"/>



Auto escaping allows automatically escaping html special characters to their asci equivalent; < becomes &lt. This can help protecting against running vulnerable code on a client web browser. Important if we allow collecting data from the user to display later on.

By default autoescaping is on.

We can turn it on and off along when opening a new autoescape block.

{% autoescape on %}
    {{  toescape }}
    {% autoescape off %}
        {{  toescape }}
    {% endautoescape %}
{% endautoescape %}

If our context data was:

'toescape': "1 is < 2"

We would output:

1 is < 2
    1 is < 2


Where escaping is currently on we can output a variable without escaping by marking it as safe

{{ value|safe}}


Where escaping is currently on we can output all elements of a collection without escaping:

{{ list_data|safeseq}}

Leave a Reply

Fill in your details below or click an icon to log in: Logo

You are commenting using your account. Log Out /  Change )

Google+ photo

You are commenting using your Google+ account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )


Connecting to %s