Template Designer Documentation
===============================
+.. highlight:: html+jinja
+
This document describes the syntax and semantics of the template engine and
will be most useful as reference to those creating Jinja templates. As the
template engine is very flexible the configuration from the application might
the template. The template syntax is heavily inspired by Django and Python.
Below is a minimal template that illustrates a few basics. We will cover
-the details later in that document:
-
-.. sourcecode:: html+jinja
+the details later in that document::
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
<html lang="en">
You can use a dot (``.``) to access attributes of a variable, alternative the
so-called "subscribe" syntax (``[]``) can be used. The following lines do
-the same:
-
-.. sourcecode:: jinja
+the same::
{{ foo.bar }}
{{ foo['bar'] }}
Tests can accept arguments too. If the test only takes one argument you can
leave out the parentheses to group them. For example the following two
-expressions do the same:
-
-.. sourcecode:: jinja
+expressions do the same::
{% if loop.index is divisibleby 3 %}
{% if loop.index is divisibleby(3) %}
To comment-out part of a line in a template, use the comment syntax which is
by default set to ``{# ... #}``. This is useful to comment out parts of the
template for debugging or to add information for other template designers or
-yourself:
-
-.. sourcecode:: jinja
+yourself::
{# note: disabled template because we no longer user this
{% for user in users %}
{% endfor %}
#}
+.. _template-inheritance:
Template Inheritance
--------------------
This template, which we'll call ``base.html``, defines a simple HTML skeleton
document that you might use for a simple two-column page. It's the job of
-"child" templates to fill the empty blocks with content:
-
-.. sourcecode:: html+jinja
+"child" templates to fill the empty blocks with content::
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
<html lang="en">
Child Template
~~~~~~~~~~~~~~
-A child template might look like this:
-
-.. sourcecode:: html+jinja
+A child template might look like this::
{% extends "base.html" %}
{% block title %}Index{% endblock %}
The filename of the template depends on the template loader. For example the
:class:`FileSystemLoader` allows you to access other templates by giving the
-filename. You can access templates in subdirectories with an slash:
-
-.. sourcecode:: jinja
+filename. You can access templates in subdirectories with an slash::
{% extends "layout/default.html" %}
parent wouldn't know which one of the blocks' content to use.
If you want to print a block multiple times you can however use the special
-`self` variable and call the block with that name:
-
-.. sourcecode:: jinja
+`self` variable and call the block with that name::
<title>{% block title %}{% endblock %}</title>
<h1>{{ self.title() }}</h1>
~~~~~~~~~~~~
It's possible to render the contents of the parent block by calling `super`.
-This gives back the results of the parent block:
-
-.. sourcecode:: jinja
+This gives back the results of the parent block::
{% block sidebar %}
<h3>Table Of Contents</h3>
macros and blocks. Control structures appear inside ``{% ... %}`` blocks
in the default syntax.
-For Loops
-~~~~~~~~~
+For
+~~~
Loop over each item in a sequece. For example, to display a list of users
-provided in a variable called `users`:
-
-.. sourcecode:: html+jinja
+provided in a variable called `users`::
<h1>Members</h1>
<ul>
+-----------------------+---------------------------------------------------+
Within a for-loop, it's psosible to cycle among a list of strings/variables
-each time through the loop by using the special `loop.cycle` helper:
-
-.. sourcecode:: html+jinja
+each time through the loop by using the special `loop.cycle` helper::
{% for row in rows %}
<li class="{{ loop.cycle('odd', 'even') }}">{{ row }}</li>
Unlike in Python it's not possible to `break` or `continue` in a loop. You
can however filter the sequence during iteration which allows you to skip
-items. The following example skips all the users which are hidden:
-
-.. sourcecode:: html+jinja
+items. The following example skips all the users which are hidden::
{% for user in users if not user.hidden %}
<li>{{ user.username|e }}</li>
If no iteration took place because the sequence was empty or the filtering
removed all the items from the sequence you can render a replacement block
-by using `else`:
-
-.. sourcecode:: html+jinja
+by using `else`::
<ul>
{% for user in users %}
The `if` statement in Jinja is comparable with the if statements of Python.
In the simplest form you can use it to test if a variable is defined, not
-empty or not false:
-
-.. sourcecode:: html+jinja
+empty or not false::
{% if users %}
<ul>
{% endif %}
For multiple branches `elif` and `else` can be used like in Python. You can
-use more complex :ref:`expressions` there too.
-
-.. sourcecode:: html+jinja
+use more complex :ref:`expressions` there too::
{% if kenny.sick %}
Kenny is sick.
:ref:`loop filtering <loop-filtering>`.
+Macros
+~~~~~~
+
+Macros comparable with functions in regular programming languages. They are
+useful to put often used idoms into reusable functions to not repeat yourself.
+
+Macros can be defined in helper templates with then are :ref:`imported <import>`
+or directly in the template where they are used. There is one big difference
+between those two possibilities. A macro that is defined in the template where
+it's also used has access to the context passed to the template. A macro
+defined in another template and then imported can only access variables defined
+there or in the global context.
+
+Here a small example of a macro that renders a form element::
+
+ {% macro input(name, value='', type='text', size=20) -%}
+ <input type="{{ type }}" name="{{ name }}" value="{{
+ value|e }}" size="{{ size }}">
+ {%- endmacro %}
+
+The macro can then be called like a function in the namespace::
+
+ <p>{{ input('username') }}</p>
+ <p>{{ input('password', type='password') }}</p>
+
+If the macro was defiend in a different template you have to
+:ref:`import <import>` it first.
+
+Inside macros you have access to three special variables:
+
+`varargs`
+ If more positional arguments are passed to the macro then accepted by the
+ macro they end up in the special `varargs` variable as list of values.
+
+`kwargs`
+ Like `varargs` but for keyword arguments. All unconsumed keyword
+ arguments are stored in this special variable.
+
+`caller`
+ If the macro was called from a :ref:`call` tag the caller is stored in
+ this variable as macro which can be called.
+
+Macros also expose some of their internal details. The following attributes
+are available on a macro object:
+
+`name`
+ The name of the macro. ``{{ input.name }}`` will print ``input``.
+
+`arguments`
+ A tuple of the names of arguments the macro accepts.
+
+`defaults`
+ A tuple of default values.
+
+`catch_kwargs`
+ This is `true` if the macro accepts extra keyword arguments (ie: accesses
+ the special `kwargs` variable).
+
+`catch_varargs`
+ This is `true` if the macro accepts extra positional arguments (ie:
+ accesses the special `varargs` variable).
+
+`caller`
+ This is `true` if the macro accesses the special `caller` variable and may
+ be called from a :ref:`call` tag.
+
+
+.. _call:
+
+Call
+~~~~
+
+In some cases it can be useful to pass a macro to another macro. For this
+purpose you can use the special `call` block. The following example shows
+a macro that takes advantage of the call functionality and how it can be
+used::
+
+ {% macro render_dialog(title, class='dialog') -%}
+ <div class="{{ class }}">
+ <h2>{{ title }}</h2>
+ <div class="contents">
+ {{ caller() }}
+ </div>
+ </div>
+ {%- endmacro %}
+
+ {% call render_dialog('Hello World') %}
+ This is a simple dialog rendered by using a macro and
+ a call block.
+ {% endcall %}
+
+It's also possible to pass arguments back to the call block. This makes it
+useful as replacement for loops. It is however not possible to call a
+call block with another call block.
+
+Here an example of how a call block can be used with arguments::
+
+ {% macro dump_users(users) -%}
+ <ul>
+ {%- for user in users %}
+ <li><p>{{ user.username|e }}</p>{{ caller(user) }}</li>
+ {%- endfor %}
+ </ul>
+ {%- endmacro %}
+
+ {% call(user) dump_users(list_of_user) %}
+ <dl>
+ <dl>Realname</dl>
+ <dd>{{ user.realname|e }}</dd>
+ <dl>Description</dl>
+ <dd>{{ user.description }}</dd>
+ </dl>
+ {% endcall %}
+
+
+Assignments
+~~~~~~~~~~~
+
+Inside code blocks you can also assign values to variables. Assignments at
+top level (outside of blocks, macros or loops) are exported from the template
+like top level macros and can be imported by other templates.
+
+Assignments are just written in code blocks like any other statement just
+without explicit keyword::
+
+ {% navigation = [('index.html', 'Index'), ('about.html', 'About')] %}
+
+
+Extends
+~~~~~~~
+
+The `extends` tag can be used to extend a template from another one. You
+can have multiple of them in a file but only one of them may be executed
+at the time. There is no support for multiple inheritance. See the section
+about :ref:`template-inheritance` above.
+
+
+Block
+~~~~~
+
+Blocks are used for inheritance and act as placeholders and replacements
+at the same time. They are documented in detail as part of the section
+about :ref:`template-inheritance`.
+
+
+Include
+~~~~~~~
+
+The `include` statement is useful to include a template and return the
+rendered contents of that file into the current namespace::
+
+ {% include 'header.html' %}
+ Body
+ {% include 'footer.html' %}
+
+Included templates have access to the current template variables minus local
+modifications.
+
+.. _import:
+
+Import
+~~~~~~
+
+Jinja2 supports putting often used code into macros. These macros can go into
+different templates and get imported from there. This works similar to the
+import statements in Python. It's important to know that imports are cached
+and imported templates don't have access to the current template variables,
+just the globals.
+
+There are two ways to import templates. You can import the complete template
+into a variable or request specific macros / exported variables from it.
+
+Imagine we have a helper module that renders forms (called `forms.html`)::
+
+ {% 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 %}
+
+The easiest and most flexible is importing the whole module into a variable.
+That way you can access the attributes::
+
+ {% 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>
+
+
+Alternatively you can import names from the template into the current
+namespace::
+
+ {% 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>
+
+
.. _expressions:
Expressions
['list', 'of', 'objects']:
Everything between two brackets is a list. Lists are useful to store
sequential data in or to iterate over them. For example you can easily
- create a list of links using lists and tuples with a for loop.
-
- .. sourcecode:: html+jinja
+ create a list of links using lists and tuples with a for loop::
<ul>
{% for href, caption in [('index.html', 'Index'), ('about.html', 'About'),
It is also possible to use inline `if` expressions. These are useful in some
situations. For example you can use this to extend from one template if a
-variable is defined, otherwise from the default layout template:
-
-.. sourcecode:: jinja
+variable is defined, otherwise from the default layout template::
{% extends layout_template if layout_template is defined else 'master.html' %}
+The general syntax is ``<do something> if <something is true> else <do
+something else>``.
+
.. _builtin-filters: