8. Templates with Jinja2

8.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

8.2. Syntax

Tab. 8.1. Jinja2 Syntax
Syntax Description
{% ... %} Statements
{{ ... }} Expressions to print to the template output
{# ... #} Comments not included in the template output
#  ... ## Line Statements

8.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>

8.4. Method Calls

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

8.5. Filters

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

8.6. Assignments

{% 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 %}

8.7. Include

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

8.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 %}

8.9. Loops

<ul>
{% for item in seq %}
    <li>{{ item }}</li>
{% endfor %}
</ul>
{% for item in items %}
    {{ item }}
{% else %}
    No items!
{% endfor %}
Tab. 8.2. 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)

8.10. Blocks

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

8.11. Cycle

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

8.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 %}

8.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>

8.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!') }}