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

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 %} with and, or and not operators.- The {% if %} statement also supports the and, or and not 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 the in and not in operators to verify the presence of a constant or variable. For example {% if "mocha" in drinks %} tests if the value "mocha" is in the drinks list variable or {% if 2 not in stores %} tests if the value 2 is not in the stores list variable. Alhough the in and not 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
NESTED JINJA LOOPS: USE REFERENCE VARIABLE & CYCLE VARIABLE

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 %} with if.- The {% for %} statement also supports the inclusion of if 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 an if clause is {% for <reference> in <variable> if <test_for_reference>%} (e.g. {% for drink in drinks if drink not in ['Cappuccino'] %} )

{% for %} with recursive 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).- The range 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)).- The cycler function lets you cycle among a series of values. It works just like the loop.cycle variable available in {% for %} loops, except the cycler function can be used outside loops. The cycler function uses its next() method to advance one item, the reset() 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).- The joiner 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 can also define content blocks. For example, the statement {% set advertisement %}<div class'banner'><img src=.....></div>{% endset %}, creates the variable advertisement 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 -- provide more advanced re-use functionality for content blocks.

{% do %} (This statement requires enabling the built-in jinja2.ext.do extension, see the recipe Enable 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 the myvar 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 %} the lower filter is applied to all content between this statement and the {% endfilter %} statement -- note the lower filter statement converts all content to lowercase, the next major section in this chapter describes Jinja's built-in filters.

dict (Function).- The dict function offers an alternative to define dictionaries without literals (e.g. {'id':1} is equivalent to dict(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 arguments true or false. 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).- The lipsum function is used to display random latin text, which is useful for filler on templates. The lipsum function is called with four parameters: lipsum(n=5, html=True, min=20, max=100). Where n is a number of paragraphs to generate, if not provided the default n is 5; html defaults to True to return HTML or you can set it to False to return regular text; and min and max 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 keyword without context to the end of the {% include %} statement (e.g. {% from 'footer.html' without context %}). In addition, the {% include %} statement also accepts the ignore 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 the caller() method within a {% macro %} statement. If you define a {% macro %} statement with a caller() 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 the caller() 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 the from and as 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 keyword with 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.