3.18. Templates with Jinja2

3.18.1. Rationale

  • 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

3.18.2. Syntax

  • {% ... %} - Statements

  • {{ ... }} - Expressions to print to the template output

  • {# ... #} - Comments not included in the template output

  • # ... ## - Line Statements

3.18.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.firstname }}</td>
                <td>{{ user.lastname }}</td>

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

    <tbody>
</table>

3.18.4. Method Calls

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

3.18.5. Filters

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

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

3.18.7. Include

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

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

3.18.9. Loops

  • 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)

<ul>
{% for item in seq %}
    <li>{{ item }}</li>
{% endfor %}
</ul>
{% for item in items %}
    {{ item }}
{% else %}
    No items!
{% endfor %}

3.18.10. Blocks

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

3.18.11. Cycle

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

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

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

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