6. Templates with Jinja2

6.1. Why?

  • A Jinja template is simply a text file.

  • Jinja can generate any text-based format (HTML, XML, CSV, LaTeX, etc.)

  • A Jinja template doesn’t need to have a specific extension: .html, .xml, or any other extension is just fine

6.2. Syntax

Table 67. Jinja2 Syntax

Syntax

Description

{% ... %}

Statements

{{ ... }}

Expressions to print to the template output

{# ... #}

Comments not included in the template output

#  ... ##

Line Statements

6.3. Example usage

<h1>List of users</h1>

<table>
    <thead>
        <tr>
            <th>First Name</th>
            <th>Last Name</th>
            <th>Role</th>
        </tr>
    </thead>

    <tbody>

        {% for user in users %}
            <tr>
                <td>{{ user.first_name }}</td>
                <td>{{ user.last_name }}</td>

                {% if user.role == 'admin' %}
                    <td>Administrator</td>
                {% else %}
                    <td>User</td>
                {% endif %}
            </tr>
        {% endfor %}

    <tbody>
</table>

6.4. Method Calls

{% for page in user.get_created_pages() %}
    ...
{% endfor %}

6.5. Filters

{{ items|join(', ') }}
{% filter upper %}
    This text becomes uppercase
{% endfilter %}

6.6. Assignment tag

{% set navigation = [('index.html', 'Index'), ('about.html', 'About')] %}
{% set key, value = call_something() %}
{% set navigation %}
    <li><a href="/">Index</a>
    <li><a href="/downloads">Downloads</a>
{% endset %}
{% set reply | wordwrap %}
    You wrote:
    {{ message }}
{% endset %}

6.7. Include

{% include 'header.html' %}
    Body
{% include 'footer.html' %}
{% for box in boxes %}
    {% include "render_box.html" %}
{% endfor %}

6.8. Conditionals

{% if loop.index is divisibleby 3 %}
{% if loop.index is divisibleby(3) %}
{% if users %}
<ul>
{% for user in users %}
    <li>{{ user.username|e }}</li>
{% endfor %}
</ul>
{% endif %}
{% if kenny.sick %}
    Kenny is sick.
{% elif kenny.dead %}
    You killed Kenny!  You bastard!!!
{% else %}
    Kenny looks okay --- so far
{% endif %}
{% if user.user_id is odd %}
    {{ user.username|e }} is odd
{% else %}
    hmm. {{ user.username|e }} looks pretty normal
{% endif %}

6.9. Loops

<ul>
{% for item in seq %}
    <li>{{ item }}</li>
{% endfor %}
</ul>
{% for item in items %}
    {{ item }}
{% else %}
    No items!
{% endfor %}
Table 68. Loops special variables

Variable

Description

loop.index

The current iteration of the loop. (1 indexed)

loop.index0

The current iteration of the loop. (0 indexed)

loop.revindex

The number of iterations from the end of the loop (1 indexed)

loop.revindex0

The number of iterations from the end of the loop (0 indexed)

loop.first

True if first iteration.

loop.last

True if last iteration.

loop.length

The number of items in the sequence.

loop.cycle

A helper function to cycle between a list of sequences. See the explanation below.

loop.depth

Indicates how deep in a recursive loop the rendering currently is. Starts at level 1

loop.depth0

Indicates how deep in a recursive loop the rendering currently is. Starts at level 0

loop.previtem

The item from the previous iteration of the loop. Undefined during the first iteration

loop.nextitem

The item from the following iteration of the loop. Undefined during the last iteration

loop.change

True if previously called with a different value (or not called at all)

6.10. Blocks

<title>{% block title %}{% endblock %}</title>
<h1>{{ self.title() }}</h1>
{% block body %}{% endblock %}
{% block body %}
    <h3>Table Of Contents</h3>
    ...
    {{ super() }}
{% endblock %}

6.11. Cycle

{% for user in users %}
    <li class="{{ loop.cycle('odd', 'even') }}">{{ user }}</li>
{% endfor %}

6.12. Base Template

<!DOCTYPE html>
<html lang="en">
<head>
    {% block head %}
    <link rel="stylesheet" href="style.css" />
    <title>{% block title %}{% endblock %} - My Webpage</title>
    {% endblock %}
</head>
<body>
    <div id="content">{% block content %}{% endblock %}</div>
    <div id="footer">
        {% block footer %}
        &copy; Copyright 2008 by <a href="http://domain.invalid/">you</a>.
        {% endblock %}
    </div>
</body>
</html>
{% extends "base.html" %}
{% block title %}Index{% endblock %}
{% block head %}
    {{ super() }}
    <style type="text/css">
        .important { color: #336699; }
    </style>
{% endblock %}
{% block content %}
    <h1>Index</h1>
    <p class="important">
      Welcome to my awesome homepage.
    </p>
{% endblock %}

6.13. Import Macros

{% macro input(name, value='', type='text') -%}
    <input type="{{ type }}" value="{{ value|e }}" name="{{ name }}">
{%- endmacro %}

{%- macro textarea(name, value='', rows=10, cols=40) -%}
    <textarea name="{{ name }}" rows="{{ rows }}" cols="{{ cols
        }}">{{ value|e }}</textarea>
{%- endmacro %}
{% import 'forms.html' as forms %}
<dl>
    <dt>Username</dt>
    <dd>{{ forms.input('username') }}</dd>
    <dt>Password</dt>
    <dd>{{ forms.input('password', type='password') }}</dd>
</dl>
<p>{{ forms.textarea('comment') }}</p>
{% from 'forms.html' import input as input_field, textarea %}
<dl>
    <dt>Username</dt>
    <dd>{{ input_field('username') }}</dd>
    <dt>Password</dt>
    <dd>{{ input_field('password', type='password') }}</dd>
</dl>
<p>{{ textarea('comment') }}</p>

6.14. i18n Trans

<p>{% trans %}Hello {{ user }}!{% endtrans %}</p>
{% trans count=list|length %}
There is {{ count }} {{ name }} object.
{% pluralize %}
There are {{ count }} {{ name }} objects.
{% endtrans %}
{{ _('Hello World!') }}