Jinja built-in statements/tags and functions (like Django template tags)
Jinja offers several built-in statements/tags that offer immediate access to elaborate operations on Jinja templates. I'll classify each of these built-in statements/tags and functions into sections so it's easier to identify them, note I'll add the reference (Function) to indicate it's referring to a Jinja function. The categories I'll use are: Comparison operations, loops, Python & filter operations, spacing & special characters and template structures.
Comparison operations
{% if %}
with{% elif %}
{% else %}
.- The{% if %}
statement is the primary building block to evaluate conditions. The{% if %}
statement is typically used in conjunction with the{% elif %}
and{% else %}
statements to evaluate more than one condition. An{% if %}
statement with an argument variable evaluates to true if a variable exists and is not empty or if the variable holds a True boolean value. Listing 4-14 illustrates a series of{% if %}
statement examples.
Listing 4-14. Jinja {% if %} statement with {% elif %} and {% else %}
{% if drinks %} {% if drinks %} {% if drinks %} We have drinks! We have drinks We have drinks {% endif %} {% else %} {% elif drinks_on_sale %} No drinks,sorry We have drinks on sale! {% endif %} {% else %} No drinks, sorry {% endif %}
Note A variable must both exist and not be empty to match a condition. A variable that just exists and is empty does not match a condition.
{% if %}
withand
,or
andnot
operators.- The{% if %}
statement also supports theand
,or
andnot
operators to create more elaborate conditions. These operators allow you to compare if more than one variable is not empty (e.g.{% if drinks and drinks_on_sale %}
), if one or another variable is not empty (e.g.{% if drinks or drinks_on_sale %}
) or if a variable is empty (e.g.{% if not drinks %}
).{% if %}
with==
,!=
,<
,>
,<=
and>=
operators.- The{% if %}
statement also supports equal, not equal, larger than and less than operators to create conditions that compare variables to fixed strings or numbers. These operators allow you to compare if a variable equals a string or number (e.g.{% if drink == "mocha" %}
), if a variable does not equal a variable or number (e.g.{% if store_id != 2 %}
) or if a variable is greater than or lesser than a number (e.g.{% if store_id > 5 %}
).{% if <value> in %}
and{% if <value> not in %}
.- The{% if %}
statement also supports thein
andnot in
operators to verify the presence of a constant or variable. For example{% if "mocha" in drinks %}
tests if the value"mocha"
is in thedrinks
list variable or{% if 2 not in stores %}
tests if the value2
is not in thestores
list variable. Alhough thein
andnot in
operators are commonly used to test list variables, it's also possible to test the presence of characters on strings (e.g.{% if "m" in drink %}
). In addition, it's also possible to compare if the value of one variable is present in another variable (e.g.{% if order_drink in drinks %}
).
Loops
{% for %}
and{% for %}
with{% else %}
.- The{% for %}
statement iterates over items on a dictionary, list, tuple or string variable. The{% for %}
statement syntax is{% for <reference> in <variable> %}
, where<reference>
is assigned a new value from<variable>
on each iteration. Depending on the nature of a variable there can be one or more references (e.g. for a list one reference, for a dictionary two references).The{% for %}
statement also supports the{% else %}
statement which is processed in case there are no iterations in a loop (i.e. the main variable is empty). Listing 4-15 illustrates a{% for %}
and a{% for %}
and{% else %}
loop example.
Listing 4-15 Jinja {% for %} statement and {% for %} with {% else %}
<ul> <ul> {% for drink in drinks %} {% for storeid,store in stores %} <li>{{ drink.name }}</li> <li><a href="/stores/{{storeid}}/">{{store.name}}</a></li> {% else %} {% endfor %} <li>No drinks, sorry</li> </ul> {% endfor %} </ul>
The {% for %}
statement also generates a series of variables to manage the
iteration process, such as an iteration counter, a first iteration
flag and a last iteration flag. Table 4-1 illustrates the {%
for %}
statement variables.
Table 4-1. Jinja {% for %} statement 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 it's the first time through the loop |
loop.last |
True if it's the last time through the loop |
loop.length |
The number of items in the sequence. |
loop.cycle |
A helper function to cycle between a list of sequences. |
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 in the loop. Undefined in the first iteration |
loop.nextitem |
The item from the following iteration in the loop. Undefined in the last iteration |
loop.changed(reference) |
Evaluates if the provided reference has changed from a prior iteration in the loop.
{% for element in list %} {% if loop.changed(element) %} <li>{{element}} changed! {% else %} <li>{{element}} did not change {% endif %} {% endfor %} |
On certain occasions you may need to nest multiple {% for %} statements and access parent loop items. In Django templates, this is easy because there's a variable for just this purpose. However, Jinja templates don't have this variable as you can see in table 4-1. A solution in Jinja templates is to define a reference variable with {% set %} before entering the child loop to gain access to the parent loop, as illustrated in the following snippet
<ul> {% for chapter in chapters %} {% set chapterloop = loop %} {% for section in chapter %} <li> {{chapterloop.index }}.{{ loop.index }}">{{ section }}</li> {% endfor %} {% endfor %} </ul>Another nested loop feature in Jinja templates is cycle, which does not exist in Django templates (as a variable at least, it does exist as a tag). The primary use of cycle is to define CSS classes so each iteration receives a different CSS class and upon rendering each iteration is displayed in a different color. The following snippet illustrates the use of the cycle variable.
{% for drink in drinks %} <li class="{{ loop.cycle('odd','even') }}">{{ drink.name }}</li> {% endfor %}Note cycle can iterate sequentially over any number of strings or variables (e.g. {{ loop.cycle('red' 'white' 'blue') }}).
{% for %}
withif
.- The{% for %}
statement also supports the inclusion ofif
statements to filter the iteration over a dictionary, list, tuple or string variable. In this manner you can limit the iteration to elements that pass or fail a certain criteria. The{% for %}
statement syntax with anif
clause is{% for <reference> in <variable> if <test_for_reference>%}
(e.g.{% for drink in drinks if drink not in ['Cappuccino'] %}
){% for %}
withrecursive
keyword.- The{% for %}
statement also supports recursion over nested dictionaries, lists, tuples or string variables. Instead of creating multiple nested{% for %}
statements, you can use recursion to re-use the same layout over each of the nested structures. Listing 4-16 illustrates a sample of a recursive loop in Jinja.
Listing 4-16. Jinja {% for %} statement with recursive keyword
# Dictionary definition coffees={ 'espresso': {'nothing else':'Espresso', 'water': 'Americano', 'steamed milk': {'more steamed milk than milk foam': 'Latte', 'chocolate syrup': {'Whipped cream': 'Mocha'} }, 'more milk foam than steamed milk': 'Capuccino' } } # Template definition with for and recursive {% for ingredient,result in coffees.iteritems() recursive %} <li>{{ ingredient }} {% if result is mapping %} <ul>{{ loop(result.iteritems()) }}</ul> {% else %} YOU GET: {{ result }} {% endif %}</li> {% endfor %} # Output espresso water YOU GET: Americano steamed milk more steamed milk than milk foam YOU GET: Latte chocolate syrup Whipped cream YOU GET: Mocha more milk foam than steamed milk YOU GET: Capuccino nothing else YOU GET: Espresso
{% break %}
and{% continue %}
.- The{% break %}
and{% continue %}
statements are available inside{% for %}
statements and allow you to break out of the loop or continue to the next iteration, just like the same keywords available in regular Python loops.
Note {% break %} and {% continue %} require enabling the built-in jinja2.ext.loopcontrols extension. See the second to last section in this chapter on how to enable Jinja extensions for more details.
range
(Function).- Therange
function works just like Python's standard function and is useful when you want to generate a loop over a given range of numbers from i to j-1. For example,range(0,5)
generates the range[0,1,2,3,4]
. In addition, the range function also supports overriding the step count -- which defaults to 1 -- in the third position (e.g.range(0,11,2)
generates[0,2,4,6,8,10]
).cycler
(Function)).- Thecycler
function lets you cycle among a series of values. It works just like theloop.cycle
variable available in{% for %}
loops, except thecycler
function can be used outside loops. Thecycler
function uses itsnext()
method to advance one item, thereset()
method to cycle to the first item and the current attribute to return the current item. Listing 4-17 illustrates a cycler method definition with CSS classes, which is then used over multiple{% for %}
loops to define a list where each item is assigned a different CSS class based on the cycle iteration.
Listing 4-17 Jinja cycler function
{% set row_class = cycler('white','lightgrey','grey') %} <ul> {% for item in items %} <li class="{{ row_class.next() }}">{{ item }}</li> {% endfor %} {% for otheritem in moreitems %} <li class="{{ row_class.next() }}">{{ otheritem }}</li> {% endfor %} # Output <ul> <li class="white">Item 1</li> <li class="lightgrey">Item 2 </li> <li class="grey">Item 3 </li> <li class="white">Item 4</li> <li class="lightgrey">Item 5</li> <li class="grey">Other item 1</li> <li class="white">Other item 2</li> </ul>
joiner
(Function).- Thejoiner
function lets you join a series of disparate sections and join them with a given separator, which defaults to a comma-space (",
"). A characteristic of the joiner function is that it returns the separator string every time it's called, except the first time to give the correct appearance in case sections are dependent on a condition. Listing 4-18 illustrates a joiner method definition with a slash-space ("/
") as its separator, which is then used to join a list of sections.
Listing 4-18 Jinja joiner function
{% set slash_joiner = joiner("/ ") %} User: {% if username %} {{ slash_joiner() }} {{username}} {% endif %} {% if alias %} {{ slash_joiner() }} {{alias}} {% endif %} {% if nickname %} {{ slash_joiner() }} {{nickname}} {% endif %} # Output # If all variables are defined User: username / alias / nickname # If only nickname is defined User: nickname # If only username and alias is defined User: username / alias # Etc, the joiner function avoids any unnecessary preceding slash # because it doesn't print anything the first time its called
Python and filter operations
{% set %}
.- The{% set %}
statement lets you define variables in the context of Jinja templates. It's useful when you need to create variables for values that aren't exposed by a Django view method or when a variable is tied to a heavyweight operation. The following is a sample statement of this statement{% set drinkwithtax=drink.cost*1.07 %}
. The scope of a variable defined in a{% set %}
statement is from its declaration until the end of the template.The
{% set %}
statement also supports thenamespace
function to hold more complex variable values (i.e. objects) and be able to modify and add values throughout its scope. For example,{% set ns = namespace(name="outer") %}
allows you to access and update thens.name
object value, as well as add new values to thens
object in other{% set %}
statements to access elsewhere in the scope (e.g. nested loops).The
{% set %}
statement can also define content blocks. For example, the statement{% set advertisement %}<div class'banner'><img src=.....></div>{% endset %}
, creates the variableadvertisement
with the content enclosed between{% set %}
and{% endset %}
which can later be reused in other parts of a template (e.g.{{advertisement}}
). The built-in{% macro %}
statement -- described in the template structures section -- provides more advanced re-use functionality for content blocks.The
{% set %}
statement when applied to content blocks also supports filters through the| <filter_name>
syntax. For example, the statement{% set dummytext | wordwrap %}Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.{% endset %}
, applies thewordwrap
filter to the content defined within the{% set %}
and{% endset %}
statements.{% do %}
(This statement requires enabling the built-in jinja2.ext.do extension, see the section on Jinja extensions for more details).- The{% do %}
statement is an expression evaluator that works like the{{ }}
variable syntax, except it doesn't produce output. For example, to increment the value of a variable or add a new element without producing any output, you can use the{% do %}
statement (e.g.{% do itemlist.append('Forgot to add this other item') %}
).
Note {% break %} and {% continue %} require enabling the built-in jinja2.ext.loopcontrols extension. See the second to last section in this chapter on how to enable Jinja extensions for more details.
{% with %}
.- The{% with %}
statement is similar to the{% set %}
statement, the only difference is the{% with %}
statement limits the scope of a variable with the{% endwith %}
statement (e.g.{% with myvar=1 %}...{% endwith %}
any elements declared in ... have access to themyvar
variable). It's also valid to declare{% set %}
statements within{% with %}
and{% endwith %}
statements to limit the scope of variables (e.g.{% with %}{% set myvar=1 %}...{% endwith %}
).
Note {% with %} requires enabling the built-in jinja2.ext.with_ extension. See the second to last section in this chapter on how to enable Jinja extensions for more details.
{% filter %}
.- The{% filter %}
statement is used to apply Jinja filters to template sections. By default, Jinja filters are applied individually to template variables, but sometimes it can be helpful to apply Jinja filters to entire template sections. For example, if you declare{% filter lower %}
thelower
filter is applied to all content between this statement and the{% endfilter %}
statement -- note thelower
filter statement converts all content to lowercase, the next major section in this chapter describes Jinja's built-in filters.dict
(Function).- Thedict
function offers an alternative to define dictionaries without literals (e.g.{'id':1}
is equivalent todict(id=1)
).
Spacing and special characters
By default, Jinja keeps all spacing (e.g. tabs, spaces, newlines) unchanged from how they are defined in a template. Figure 4-1 illustrates the default rendering of a template snippet in Jinja.
Figure 4-1 Default space rendering in Jinja template
As you can see in figure 4-1, the
spacing before, after and by the {% for %}
and
{% if %}
statements themselves is generated as is.
While this spacing is natural, it can be beneficial to create more
compact outputs with templates that handle a lot of data. The minus
sign -
appended to either the start or end of a
statement (e.g. {%- <statement> -%}
) tells Jinja
to strip the new line that follows it. This is best illustrated
with the examples presented in figure 4-2 and figure 4-3.
Figure 4-2. Space rendering in Jinja template with single -
Figure 4-3. Space rendering in Jinja template with double -
As you can see in figure 4-2, the
-
symbol before closing the {% for %}
statement makes Jinja eliminate the new line after each iteration.
In the case of the {% if %}
statement also in figure
4-2, the -
symbol has no impact because there's no new
line associated with the statement. In figure 4-3 you can see
there's an additional -
symbol at the start of the
{% endfor %}
statement which makes Jinja eliminate the
new line before the start of each iteration. In the case of the
{% if %}
statement also is figure 4-3, the additional
-
symbol has no impact because there's no new line
associated with the statement.
Because adding -
symbols to every Jinja statement can become tiresome, you can
configure Jinja so that by default it uses this behavior (i.e. just
as if you added -
). To alter Jinja's default spacing
behavior, you can use two Jinja environment parameters :
trim_blocks
and lstrip_blocks
, both of
which default to False
. Note that in Django you set up
Jinja environment parameters as part of the OPTIONS
variable in settings.py
, as described in the prior
section on setting up Jinja template configuration in Django.
Figure 4-4 illustrates the
rendering of a code snippet when trim_blocks
is set to
True
, where as figure 4-5 illustrates the rendering of
a code snippet when both trim_blocks
and
lstrip_blocks
are set to True
.
Figure 4-4. Space rendering in Jinja template with trim_blocks
Figure 4-5. Space rendering in Jinja template with both trim_blocks and lstrip_blocks set to True
As you can see in figures 4-4 and
4-5, the rendering produced by changing the
trim_blocks
and lstrip_blocks
Jinja
environment variables is very similar to that of using
-
symbols to start and end Jinja statements. It's
worth mentioning that if you set lstrip_blocks
to
True
and want to omit its behavior for certain
sections, you can do so by adding the plus sign +
to
either the start or end of a statement -- just like you use the
minus sign -
to achieve its opposite behavior.
{% raw %}
.- The{% raw %}
statement is used to output any Jinja reserved characters verbatim until the{% endraw %}
statement is reached. The{% raw %}
statement is ideal if you want to render large chunks of Jinja template code or if you have a lot of text that includes special Jinja template characters (e.g.{{
,{%
)
Tip You can output special Jinja template characters individually by quoting them as part of a hard-coded string variable (e.g. to output {{ use {{ '{{' }}) vs. using a {% raw %} statement.
{% autoescape %}
.- The{% autoescape %}
statement lets you escape HTML characters from a template section, effectively overriding Django's Jinja default autoescaping behavior. The{% autoescape %}
accepts one of two argumentstrue
orfalse
. With{% autoescape true %}
all template content between this statement and the{% endautoescape %}
statement is HTML escaped, with{% autoescape false %}
no template content between this statement and the{% endautoescape %}
statement is HTML escaped.
Note {% autoescape %} requires enabling the built-in jinja2.ext.autoescape extension. See the second to last section in this chapter on how to enable Jinja extensions for more details.
lipsum
(Function).- Thelipsum
function is used to display random latin text, which is useful for filler on templates. Thelipsum
function is called with four parameters:lipsum(n=5, html=True, min=20, max=100)
. Wheren
is a number of paragraphs to generate, if not provided the defaultn
is5
;html
defaults toTrue
to return HTML or you can set it toFalse
to return regular text; andmin
andmax
represent the minimum and maximum number of random words per paragraph. To use the lipsum function you simply define a variable with it and the output to generate the random latin text (e.g.{% set latinblurb=lipsum() %}
and then{{latinblurb}}
to output the random latin text).
Template structures
{% block %}
.- The{% block %}
statement is used to define page sections that can be overridden on different Jinja templates. See the previous section on creating reusable Jinja templates for detailed examples of this statement.{# #}
.- The{#}
statement is used to enclose comments on Jinja templates. Any content placed between{#
and#}
is bypassed by Jinja and doesn't appear in the final rendered web page.{% extends %}
.- The{% extends %}
statement is used to reuse the layout of another Jinja template. See the previous section on creating reusable Jinja templates for detailed examples of this statement.{% include %}
.- The{% include %}
statement is used to embed a Jinja template in another Jinja template. Note that by default, the{% include %}
statement gets access to the current template instance values (i.e. its context). If you want to disable access to a template's context you can use the{% import %}
statement or pass the keywordwithout context
to the end of the{% include %}
statement (e.g.{% from 'footer.html' without context %}
). In addition, the{% include %}
statement also accepts theignore missing
keyword which tells Jinja to ignore the statement if the template to be included does not exist. See the previous section on creating reusable Jinja templates for detailed examples of this statement.{% macro %}
.- The{% macro %}
statement is a template function designed to output content. It's ideal for repetitive content snippets, where you define a{% macro %}
statement once and execute it multiple times with different variables -- like a function -- on any template. See the previous section on creating reusable Jinja templates for detailed examples of this statement. It's also worth mentioning the built-in{% set %}
statement -- described in the Python and filter operations section -- provides simpler re-use functionality for content blocks.{% call %}
.- The{% call %}
statement is used in conjunction with the{% macro %}
statement to reference thecaller()
method within a{% macro %}
statement. If you define a{% macro %}
statement with acaller()
reference as part of its content, you can rely on the{% call %}
statement to invoke the{% macro %}
and have the contents of the{% call %}
statement substituted in place of thecaller()
method. See the previous section on creating reusable Jinja templates for detailed examples of this statement.{% import %}
and{% from ... import %}
.- The{% import %}
statement is used to access elements from other templates. Similar to Python's standard import, you can also use thefrom
andas
keywords to limit or rename the elements imported into a template. Note that by default and due to its caching behavior, the{% import %}
statement doesn't get access to the current template instance values (i.e. its context), it just gets access to globals (e.g. variables and macros). If you want to access a template's context you can use the{% include %}
statement or pass the keywordwith context
to the end of the{% import %}
statement to disable caching and access a template's context (e.g.{% from 'footer.html' with context %}
). See the previous section on creating reusable Jinja templates for detailed examples of this statement.