OverIQ.com

Basics of Jinja Template Language

Last updated on July 27, 2020


Jinja Template Language is a small set of constructs which helps us to automate the creation of templates.

Variable, Expression and Function Call #

In Jinja double curly {{ }} braces allows us to evaluate an expression, variable or function call and print the result into the template. For example:

Evaluating Expression #

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
>>>
>>> from jinja2 import Template
>>>
>>> Template("{{ 10 + 3 }}").render()
'13'
>>>
>>> Template("{{ 10 - 3 }}").render()
'7'
>>>
>>> Template("{{ 10 // 3 }}").render()
'3'
>>>
>>> Template("{{ 10 / 3 }}").render()
'3.3333333333333335'
>>>
>>> Template("{{ 10 % 3 }}").render()
'1'
>>>
>>> Template("{{ 10 ** 3 }}").render()
'1000'
>>>

Other Python operators like Comparision, Logical and Membership operators are also available inside expressions.

Evaluating Variables #

1
2
3
4
5
6
7
>>>
>>> Template("{{ var }}").render(var=12)
'12'
>>>
>>> Template("{{ var }}").render(var="hello")
'hello'
>>>

We are not limited to just numbers and strings, Jinja templates can also handle complex data structures like list, dictionary, tuple, and even custom classes.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
>>>
>>> Template("{{ var[1] }}").render(var=[1,2,3])
'2'
>>>
>>> Template("{{ var['profession'] }}").render(var={'name':'tom', 'age': 25, 'profession': 'Manager' })
'Manager'
>>>
>>>
>>> Template("{{ var[2] }}").render(var=("c", "c++", "python"))
'python'
>>>
>>>
>>> class Foo:
...    def  __str__(self):
...        return "This is an instance of Foo class"
...
>>>
>>> Template("{{ var }}").render(var=Foo())
'This is an instance of Foo class'
>>>
>>>

In case of an invalid index, Jinja will silently output an empty string.

1
2
3
4
>>>
>>> Template("{{ var[100] }}").render(var=("c", "c++", "python"))
''
>>>

Function call #

In Jinja to evaluate a function we simply call it as usual.

1
2
3
4
5
6
7
8
>>>
>>> def foo():
...    return "foo() called"
...
>>>
>>> Template("{{ foo() }}").render(foo=foo)
'foo() called'
>>>

Attributes and Method #

To access attributes and method of an object use the dot(.) operator.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
>>>
>>> class Foo:
...     def __init__(self, i):
...         self.i = i
...     def do_something(self):
...         return "do_something() called"
...
>>>
>>> Template("{{ obj.i }}").render(obj=Foo(5))
'5'
>>>
>>> Template("{{ obj.do_something() }}").render(obj=Foo(5))
'do_something() called'
>>>

Comments #

Jinja has the following syntax to add single or multiline comments inside the templates:

1
2
3
4
5
6
7
{# comment in a line #}

{#
    comment expanded 
    to multiple
    lines
#}

Setting Variables #

Inside the template, we can define a variable using the set statement.

1
2
{% set fruit = 'apple' %}
{% set name, age = 'Tom', 20 %} {# tuple unpacking works inside templates too #}

We define variables to store the result of some complex operation so that it can be reused across the template. Variables defined outside of control structures (discussed below) act as global variables and can be accessed inside any control structure. However, variables created inside control structure act as local variables and are only visible inside the control structure in which it is defined, the only exception to this rule is the if statement.

Control Structures #

Control structures allow us to add control flow and looping inside the templates. By default, control structures use {% ... %} delimiter instead of double curly braces {{ ... }}.

if statement #

The if statement in Jinja mimics the if statement in Python and value of the condition determines the flow of the statement. For example:

1
2
3
{% if bookmarks %}
    <p>User has some bookmarks</p>
{% endif %}

If the value of variable bookmarks evaluates to true then the string <p>User has some bookmarks</p> will be printed. Remember that in Jinja if a variable is not defined it evaluates to false.

We can also use elif and else clause just like regular Python code. For example:

1
2
3
4
5
6
7
8
9
{% if user.newbie %}
    <p>Display newbie stages</p>
{% elif user.pro %}
    <p>Display pro stages</p>
{% elif user.ninja %}
    <p>Display ninja stages</p>
{% else %}
    <p>You have completed all stages</p>    
{% endif %}

The control statements can also be nested. For example:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
{% if user %}
    {% if user.newbie %}
        <p>Display newbie stages</p>
    {% elif user.pro %}
        <p>Display pro stages</p>
    {% elif user.ninja %}
        <p>Display ninja stages</p>
    {% else %}
        <p>You have completed all states</p>
    {% endif %}
{% else %}
    <p>User is not defined</p>
{% endif %}

In certain cases, it is quite useful to add if statement in a single line. Jinja supports inline if statement but call it if expression because it is created using double curly braces {{ ... }} instead of {% ... %}. For example:

{{ "User is logged in" if loggedin else "User is not logged in"  }}

Here if the variable loggedin evaluates to true then the string "User is logged in" will be printed. Otherwise, the string "User is not logged in" will be printed.

The else clause is optional, if not provided, then the else block will be evaluated to an undefined object.

{{ "User is logged in" if loggedin  }}

Here if the loggedin variable evaluates to true then the string "User is logged in" will be printed. Otherwise, nothing will be printed.

Just like Python, we can use Comparision, Logical and Membership operators in control structures to create more complex conditions. Here are some examples:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
{# if user.count is equal to 1000, '<p>User count is 1000</p>' will be printed #}
{% if users.count == 1000 %}
    <p>User count is 1000</p>
{%  endif %}


{# expression 10 >= 2 is true so '<p>10 >= 2</p>' will be printed #}
{% if 10 >= 2 %}
    <p>10 >= 2</p>
{%  endif %}


{# expression "car" <= "train" is true so '<p>car <= train</p>' will be printed #}
{% if "car" <= "train" %}
    <p>car <= train</p>
{%  endif %}


{# 
    if user is logged in and is a superuser, 
    '<p>User is logged in and is a superuser</p>' will be printed 
#}
{% if user.loggedin and user.is_superuser %}
    <p>User is logged in and is a superuser</p>
{%  endif %}


{# 
    if user is superuser or moderator or author 
    '<a href="#">Edit</a>' will be printed 
#}
{% if user.is_superuser or user.is_moderator or user.is_author %}
    <a href="#">Edit</a>
{%  endif %}


{# 
    if user and current_user points to the same object
    <p>user and current_user are same</p> will be printed 
#}
{% if user is current_user  %}
    <p>user and current_user are same</p>
{%  endif %}


{# 
    As "Flask" is one of element in dictionary
    '<p>Flask is in the dictionary</p>' will be printed 
#}
{% if ["Flask"] in ["Django", "web2py", "Flask"] %}
    <p>Flask is in the dictionary</p>
{%  endif %}

In case your conditions are becoming too complex or you simply want to alter the operator precedence, you can wrap your expressions inside the parentheses () as follows:

1
2
3
{% if (user.marks > 80) and (user.marks < 90)  %}
    <p>You grade is B</p>
{%  endif %}

For loop #

For loop allows us to iterate over a sequence. For example:

1
2
3
4
5
6
7
{% set user_list = ['tom','jerry', 'spike'] %}

<ul>
{% for user in user_list %}
    <li>{{ user }}</li>
{% endfor %}
</ul>

Output:

<ul>

    <li>tom</li>

    <li>jerry</li>

    <li>spike</li>

</ul>

Here is how you can iterate over a dictionary:

1
2
3
4
5
6
7
{% set employee = { 'name': 'tom', 'age': 25, 'designation': 'Manager' } %}

<ul>
{% for key in employee.items() %}
    <li>{{ key }} : {{ employee[key] }}</li>
{% endfor %}
</ul>

Output:

<ul>

    <li>designation : Manager</li>

    <li>name : tom</li>

    <li>age : 25</li>

</ul>

Note: In Python elements of a dictionary are not stored in any particular order so the output may differ.

If you want to retrieve key and value of a dictionary at the same time use items() method as follows.

1
2
3
4
5
6
7
{% set employee = { 'name': 'tom', 'age': 25, 'designation': 'Manager' } %}

<ul>
{% for key, value in employee.items() %}
    <li>{{ key }} : {{ value }}</li>
{% endfor %}
</ul>

Output:

1
2
3
4
5
6
7
8
9
<ul>

    <li>designation : Manager</li>

    <li>name : tom</li>

    <li>age : 25</li>

</ul>

The for loop can also take an optional else clause just like Python, however, its usage is slightly different. Recall that in Python when a for loop is followed by an else clause, the else clause is executed only when for loop terminates after looping over the sequence or when the sequence is empty. It is not executed when the for loop is terminated by the break statement.

When else clause is used with the for loop in Jinja, it is executed only when the sequence is empty or not defined. For example:

1
2
3
4
5
6
7
8
9
{% set user_list = [] %}

<ul>
{% for user in user_list  %}
    <li>{{ user }}</li>
{% else %}
    <li>user_list is empty</li>
{% endfor %}
</ul>

Output:

<ul>

    <li>user_list is empty</li>

</ul>

Similar to nested if statement, we can also have nested for loop. In fact, we can nest any control structures inside one another.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
{% for user in user_list %}
    <p>{{ user.full_name }}</p>
    <p>
        <ul class="follower-list">
            {% for follower in user.followers %}
            <li>{{ follower }}</li>
            {% endfor %}
        </ul>
    </p>
{% endfor %}

The for loop gives you access to a special variable called loop to track the progress of the loop. For example:

1
2
3
4
5
<ul>
{% for user in user_list  %}
    <li>{{ loop.index }} - {{ user }}</li>
{% endfor %}
</ul>

The loop.index inside the for loop returns the current iteration of the loop starting with 1. The following table lists the other commonly used attributes of the loop variable.

Variable Description
loop.index0 same as loop.index but 0 indexed i.e it starts counting iteration from 0 instead of 1.
loop.revindex returns the iteration from the end of the loop (1 indexed).
loop.revindex0 same as loop.revindex but 0 indexed.
loop.first returns True if the current iteration is the first iteration. Otherwise False.
loop.last returns True if the current iteration is the last iteration. Otherwise False.
loop.length returns the length of the sequence the for loop is iterating over.

Note: For a complete list visit the documentation.

Filters #

Filters modify the variables before they are rendered. The syntax of using filters is as follows:

variable_or_value|filter_name

Here is an example:

{{ comment|title }}

The title filter capitalizes the first character of every word, so if the variable comment is set to "dust in the wind", then the output will be "Dust In The Wind".

We can also chain filters to fine tune the output. For example:

{{ full_name|striptags|title }}

The striptags filter removes all the HTML tags from the variable. In the above code, striptags filter will be applied first followed by the title filter.

Some filters can also accept arguments. To pass arguments to a filter call it like a function. For example:

{{ number|round(2) }}

The round filter rounds the number to the specified number of digits.

The following table lists some commonly used filters.

Name Description
upper converts all the characters to uppercase.
lower converts all the characters to lowercase.
capitalize capitalizes the first character and converts rest of the characters to lowercase.
escape escapes the value
safe prevents escaping
length returns the number of elements in the sequence
trim removes leading and trailing whitespace characters
random returns a random item from the sequence

Note: The complete list of built-in filters is available here.

Macros #

A Macro in Jinja is similar to a function in Python. The idea is to create reusable code by giving it a name. For example:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
{% macro render_posts(field, sep=False) %}
    <div>
        {% for post in post_list %}
            <h2>{{ post.title }}</h2>
            <article>
                {{ post.html|safe }}
            </article>
        {% endfor %}
        {% if sep %}<hr>{% endif %}
    </div>
{% endmacro %}

Here we have created a macro named render_posts which takes a required argument named post_list and an optional argument named sep. To use a macro call it as follows:

{{ render_posts(posts) }}

The macro definition must appear before its use otherwise you will get an error.

Rather than sprinkling macros in our templates, it is a good idea to store them in a separate file and then import the file as required.

Let's say we have stored all our macros in a file named macros.html inside the templates directory. Now to import macros from macros.html we use import statement as follows:

{% import "macros.html" as macros %}

We can now refer to macros defined inside macros.html using the macros variable. For example:

{{ macros.render_posts(posts) }}

The import statement {% import "macros.html" as macros %} imports all the macros and variables ( defined at the top level) defined inside macros.html into the template. Alternatively, we can also import selected names inside the template using the form statement as follows:

{% from "macros.html" import render_posts  %}

When using macros, you will you encounter cases where you would need to pass an arbitrary number of arguments to a macro.

Similar to *args and **kwargs in Python, inside the macro you have access to varargs and kwargs.

varargs: It captures additional position arguments passed to the macro as a tuple.

kwargs: It captures additional keyword arguments passed to the macro as a dictionary.

Although, you have access to these two variables inside macro you don't need to explicitly declare them in the macro header. Here is an example:

1
2
3
4
5
6
7
{% macro custom_renderer(para) %}
    <p>{{ para }}</p>
    <p>varargs: {{ varargs }}</p>
    <p>kwargs: {{ kwargs }}</p>
{% endmacro %}

{{ custom_renderer(10, "apple", name='spike', age=15) }}

In this case, additional positional argument i.e "apple" is assigned to varargs and additional keyword arguments ( name='spike', age=15 ) is assigned to kwargs.

Escaping #

By default, Jinja automatically escapes the variable output for security purpose. So if a variable has a string containing HTML like "<p>Escaping in Jinja</p>" then it will be rendered as "<p>&lt;p&gt;Escaping in Jinja&lt;/p&gt;</p>". This will cause the HTML tags to be displayed in the browser instead of interpreted. In case you know that the data is safe and want to render it as it is without escaping, you can use the safe filter. For example:

{% set html = <p>Escaping in Jinja</p> %}
{{ html|safe }}

Output:

<p>Escaping in Jinja</p>

Applying safe filter repeatedly to a large block of content can be awkward and inconvenient, that's why Jinja provides autoescape statement to escape or unescape large blocks of content. It accepts true or false as an argument to turn on and turn off auto-escaping respectively. For example:

1
2
3
4
5
6
7
{% autoescape true %}
    Escaping enabled
{% endautoescape %}

{% autoescape false %}
    Escaping disabled
{% endautoescape %}

Everything between {% autoescape false %} and {% endautoescape %} will be rendered as it is without escaping. If you want to escape some of the elements when auto-escaping is turned off use the escape filter. For example:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
{% autoescape false %}
    <div class="post">
        {% for post in post_list %}
            <h2>{{ post.title }}</h2>
            <article>
                {{ post.html }}
            </article>
        {% endfor %}        
    </div>
    <div>
        {% for comment in comment_list %}
            <p>{{ comment|escape }}</p>  # escaping is on for comments
        {% endfor %}
    </div>
{% endautoescape %}

Including templates #

The include statement renders a template inside another template. The include statement is commonly used to render a static section which is repeated throughout the site. Here is the syntax of the include statement:

{% include 'path/to/template' %}

Let's say we have a simple navigation bar stored in nav.html inside templates directory as follows:

1
2
3
4
5
<nav>
    <a href="/home">Home</a>
    <a href="/blog">Blog</a>
    <a href="/contact">Contact</a>  
</nav>

To include the navigation bar inside home.html, we use the following code:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

    {# including the navigation bar from nav.html #}
    {% include 'nav.html' %}   

</body>
</html>

Output:

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
</head>
<body>

<nav>
    <a href="/home">Home</a>
    <a href="/blog">Blog</a>
    <a href="/contact">Contact</a>
</nav>

</body>
</html>

Template Inheritance #

Template Inheritance is one the most powerful aspects of Jinja Templating. The idea behind template inheritance is somewhat similar to Object Oriented Programming. We start by creating a base template which contains the HTML skeleton and markers that child template can override. The markers are created using the block statement. The child templates uses extends statement to inherit or extends the base templates. Let's take an example:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
{# This is templates/base.html template #}
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>{% block title %}Default Title{% endblock %}</title>
</head>
<body>

    {% block nav %}
        <ul>
            <li><a href="/home">Home</a></li>
            <li><a href="/api">API</a></li>
        </ul>
    {% endblock %}

    {% block content %}

    {% endblock %}

</body>
</html>

This is our base template base.html. It defines three blocks using the block statement where child templates can fill in. The block statement takes a single argument which is the name of the block. Inside the template, the block name must be unique otherwise you will get an error.

A child template is a template which extends the base template. The child template may add, override or leave along the contents of the parent block. Here is how we can create a child template.

1
2
3
4
5
6
7
8
9
{# this is templates/child.html template #}
{% extends 'base.html' %}


{% block content %}
    {% for bookmark in bookmarks %}
        <p>{{ bookmark.title }}</p>
    {% endfor %}
{% endblock %}

The extends statement tells Jinja that child.html is a child template and inherits from base.html. When Jinja encounters the extends statement it loads the base template i.e base.html then replaces the blocks of content in the parent template with the blocks of content of the same name in the child template. In case, a block of matching name isn't found in the child template then the block from parent template will be used.

Note that in the child template we are only overriding the content block, so the default content from the title and nav will be used while rendering child template. The output should look like this:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
<head>
    <meta charset="UTF-8">
    <title>Default Title</title>
</head>
<body>
    
    <ul>
        <li><a href="/home">Home</a></li>
        <li><a href="/api">API</a></li>
    </ul>

    <p>Bookmark title 1</p>
    <p>Bookmark title 2</p>
    <p>Bookmark title 3</p>
    <p>Bookmark title 4</p>


</body>
</html>

If we want, we can change the default the title by overriding the title block in the child.html as follows:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
{# this is templates/child.html template #}
{% extends 'base.html' %}

{% block title %}
    Child Title 
{% endblock %}

{% block content %}
    {% for bookmark in bookmarks %}
        <p>{{ bookmark.title }}</p>
    {% endfor %}
{% endblock %}

After overriding a block you can still refer to the content in the parent template by calling super() function. The super() call is generally used when child template needs to add its own content in addition to the content from the parent. For example:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
{# this is templates/child.html template #}
{% extends 'base.html' %}

{% block title %}
    Child Title 
{% endblock %}

{% block nav %}
    {{ super() }} {# referring to the content in the parent templates #}
    <li><a href="/contact">Contact</a></li>
    <li><a href="/career">Career</a></li>
{% endblock %}

{% block content %}
    {% for bookmark in bookmarks %}
        <p>{{ bookmark.title }}</p>
    {% endfor %}
{% endblock %}

Output:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
<head>
    <meta charset="UTF-8">
    <title>Child Title</title>
</head>
<body>
    
    <ul>
        <li><a href="/home">Home</a></li>
        <li><a href="/api">API</a></li>
        <li><a href="/contact">Contact</a></li>
        <li><a href="/career">Career</a></li>
    </ul>

    <p>Bookmark title 1</p>
    <p>Bookmark title 2</p>
    <p>Bookmark title 3</p>
    <p>Bookmark title 4</p>


</body>
</html>

That's all you need to know about Jinja templates. In the upcoming lessons, we will use this knowledge to create some great templates.