Jinja2: Difference between revisions

From NovaOrdis Knowledge Base
Jump to navigation Jump to search
 
(104 intermediate revisions by the same user not shown)
Line 1: Line 1:
=External=
=External=
* https://jinja.palletsprojects.com/en/latest/
* https://medium.com/knoldus/jinja2-template-the-modern-design-friendly-templating-engine-a9218fec96e5
* https://medium.com/knoldus/jinja2-template-the-modern-design-friendly-templating-engine-a9218fec96e5
* https://jinja.palletsprojects.com/en/latest/
* https://pypi.org/project/Jinja2/
* https://pypi.org/project/Jinja2/


Line 7: Line 7:
* [[Templating_in_Python|Templating in Python]]
* [[Templating_in_Python|Templating in Python]]
* [[Templating]]
* [[Templating]]
* [[Jinja Filters]]


=TODO=
=Overview=
Jinja2 is a templating engine. Placeholders in the template allow writing code that has a syntax similar to Python. The engine ingests the template and data and renders the final document. Jinja2 provides template inheritance and inclusion. Jinja2 allows definition and import of macros within the template. Templated are compiled to Python code and cached, or they can be compiled ahead of time. Jinja2 provides extensible filters, tests, functions and syntax.


<font color=darkkhaki>
Jinja’s philosophy is that while application logic belongs in Python if possible, it shouldn’t make the template designer’s job difficult by restricting functionality too much.
What happens if a document containing a variable is rendered and the variable value is not provided?
</font>
 
=Overview=
=Playground=
{{External|https://github.com/ovidiuf/playground/tree/master/pyhton/jinja2}}


=Programming Model=
=Programming Model=
Line 24: Line 18:
Then add this to your <code>requirements.txt</code>
Then add this to your <code>requirements.txt</code>
<syntaxhighlight lang='text'>
<syntaxhighlight lang='text'>
jinja2 == 3.0.3
jinja2 == 3.1.2
</syntaxhighlight>
</syntaxhighlight>


Once the virtual environment is updated, use it as such:
==Read the Template from File==
<syntaxhighlight lang='py'>
<syntaxhighlight lang='py'>
from pathlib import Path
from pathlib import Path
from jinja2 import Environment, FileSystemLoader
from jinja2 import Environment, FileSystemLoader


template_dir = Path('...')
tdir = Path('/Users/ovidiu/tmp/jinja')
text = Environment(loader=FileSystemLoader(template_dir))\
template = Environment(loader=FileSystemLoader(tdir)).get_template('test-template.yaml.j2')
  .get_template('my-template.yaml.j2') \
 
  .render(variable_1='some value', variable_2='some other value')
text = template.render(
    variable_1='blue',
    variable_2=5
)
</syntaxhighlight>
</syntaxhighlight>


If the content is already loaded:
For a template similar to:
<syntaxhighlight lang='py'>
color: {{  variable_1 }}
size: {{  variable_2 }}
</syntaxhighlight>


the rendering looks similar to:
<syntaxhighlight lang='py'>
<syntaxhighlight lang='py'>
template_as_text = '...'
color: blue
rtemplate = Environment().from_string(template_as_text)
size: 5
data = rtemplate.render(variable_1='some value', variable_2='some other value')
</syntaxhighlight>
 
==Template as String in Memory==
<syntaxhighlight lang='py'>
template_as_text = """
color: {{  variable_1 }}
size: {{  variable_2 }}
"""
 
template = Environment().from_string(template_as_text)
 
text = template.render(
    variable_1='blue',
    variable_2=5
)
</syntaxhighlight>
</syntaxhighlight>


==Variables==
==Variables==
Variables can be provided individually as arguments of the <code>render()</code> method.
Variables can be provided individually as arguments of the <code>render()</code> method, as shown above.


Alternatively, a recursive data structure can be provided as argument.
Alternatively, a recursive data structure can be provided as argument:
<syntaxhighlight lang='py'>
data = {
    'variable_1': 'blue',
    'variable_2': 5,
}
text = template.render(data)
</syntaxhighlight>


=Templating Language=
=<span id='Variable'></span>Template Variable=
 
==Variable==
<syntaxhighlight lang='jinja'>
<syntaxhighlight lang='jinja'>
raw content followed by {{ variable1 }}
raw content followed by {{ color }}
</syntaxhighlight>
</syntaxhighlight>
==Conditional==
If the <code>color</code> variable in the example above is not defined when the template is rendered, it will be rendered as an empty string.
 
Also see [[#Variables|Variables]] above.
=<span id='Conditional'></span>Conditionals=
{{External|https://jinja.palletsprojects.com/en/latest/templates/#if-expression}}
The [[#Expressions|expressions]] described below can be used in conditionals:
<syntaxhighlight lang='jinja'>
<syntaxhighlight lang='jinja'>
{%if some_var == "something" -%}
{% if <expression> %}
something
something will be rendered here
something else {{ var_1 }}
{% elif <expression>  %}
{% elif some_var == "something else" -%}
something else will be rendered here
something else
{% else %}
{% else %}
something completely different {{ var_2 }}
and something else will be rendered here
{%- endif %}
{% endif %}
</syntaxhighlight>
</syntaxhighlight>
This works with empty strings too:
Example:
<syntaxhighlight lang='jinja'>
<syntaxhighlight lang='jinja'>
{%if some_var != "" -%}
{%- if color == 'blue' -%}
my var is {{ some_var }}
BLUE
{%- elif color == 'red'  -%}
RED
{%- else -%}
UNDEFINED
{%- endif -%}
</syntaxhighlight>
</syntaxhighlight>
 
=<span id='Loop'></span>Loops=
==<tt>defined</tt> Conditional==
To check whether a variable is defined or not defined:
<syntaxhighlight lang='jinja'>
<syntaxhighlight lang='jinja'>
{%if some_var is defined  -%}
my_list:
something
{%- for i in seq %}
{%- endif %}
  - '{{ i }}'
{%- endfor %}
</syntaxhighlight>
</syntaxhighlight>
<syntaxhighlight lang='jinja'>
where <code>seq</code> is a Python list:
{%if some_var is not defined  -%}
<syntaxhighlight lang='py'>
something else
text = Environment(...).get_template(...).render(seq=['a', 'b'])
{%- endif %}
</syntaxhighlight>
</syntaxhighlight>
 
=<span id='Macro'></span>Macros=
==Check whether a Value is None==
{{External|https://jinja.palletsprojects.com/en/latest/templates/#macros}}
 
Macros are equivalent with functions in a regular programming language:
Note that a variable that has a <code>None</code> value is still defined, so the above <code>is defined</code> test will pass.
 
To check for <code>None</code>, specifically:
<syntaxhighlight lang='jinja'>
<syntaxhighlight lang='jinja'>
{%if none(some_var) -%}
{%- macro some_macro(color, size) -%}
'som_var' is None
color: {{ color }}, size: {{ size }}
{%- endif %}
{%- endmacro -%}
</syntaxhighlight>


<font color=darkkhaki>
{{- some_macro('blue', 1) }}
The following construct broke the renderer when <code>some_var</code> was <code>None</code>:
<syntaxhighlight lang='jinja'>
{%if some_var is some_var and not none(some_var) -%}
{{ queues }}
{%- endif %}
</syntaxhighlight>
</syntaxhighlight>


Error message:
=Rendering Booleans=
 
<font color=darkkhaki>If a boolean value from Python will be rendered as having its first character capitalized. This is a problem when rendering [[YAML]] or [[JSON Concepts|JSON]]. This is something that works but I am not sure is optimal (need to adjust whitespace):
>   ???
E  TypeError: 'NoneType' object is not callable
 
</font>


==Specific Boolean Values==
To check for a specific boolean value:
<syntaxhighlight lang='jinja'>
<syntaxhighlight lang='jinja'>
{%if some_var == false  -%}
{% if value is boolean %}{{ value | lower }}
something
{% else %}{{ value }}
{%- endif %}
{% endif %}
</syntaxhighlight>
</syntaxhighlight>


==Type Conditional==
</font>
 
To check whether a value is a string:
<syntaxhighlight lang='jinja'>
{%if some_var is string  -%}
something
{%- endif %}
</syntaxhighlight>
 
For more built-in filters and tests: {{External|https://www.webforefront.com/django/usebuiltinjinjafilters.html}}
 
==Conditional Expressions==
 
<syntaxhighlight lang='jinja'>
{%if some_var == "a" and some_other_var == "b" -%}
my vars are {{ some_var }}, {{ some_other_var }}
</syntaxhighlight>


==Loops==
=Rendering Dictionaries=
<syntaxhighlight lang='jinja'>
<syntaxhighlight lang='jinja'>
my_list:
  {%- for i in seq %}
  - '{{ i }}'
  {%- endfor %}
</syntaxhighlight>
where <code>seq</code> is a Python list:
<syntaxhighlight lang='py'>
seq = ['a', 'b']
text = Environment(loader=FileSystemLoader(template_dir)).get_template('my-template.yaml.j2').render(seq=seq)
</syntaxhighlight>
==Rendering Dictionaries==
<syntaxhighlight lang='py'>
from jinja2 import Environment
template_as_text = '''
something:
something:
{%- for key, value in a_dict.items() %}
{%- for key, value in a_dict.items() %}
Line 161: Line 143:
{% endfor %}
{% endfor %}
somethingelse
somethingelse
'''
a_dict = {'a': 'A', 'b': 'B', 'c': 'C'}
rtemplate = Environment().from_string(template_as_text)
data = rtemplate.render(a_dict=a_dict)
print(data)
</syntaxhighlight>
</syntaxhighlight>
 
<syntaxhighlight lang='py'>
text = Environment().from_string(...).render(a_dict={'a': 'A', 'b': 'B', 'c': 'C'})
</syntaxhighlight>
This will render:
This will render:
<syntaxhighlight lang='text'>
<syntaxhighlight lang='text'>
Line 176: Line 155:
somethingelse
somethingelse
</syntaxhighlight>
</syntaxhighlight>
===Rendering a List of Dictionaries===
==Rendering a List of Dictionaries==
<syntaxhighlight lang='py'>
<syntaxhighlight lang='jinja'>
{% if a_list_of_maps %}
{% if a_list_of_maps %}
   some_tag_that_introduces_the_list_of_maps:
   some_tag_that_introduces_the_list_of_maps:
{% for m in a_list_of_maps %}
{%- for m in a_list_of_maps %}
    -
    - {%- for k, v in m.items() %}
{% for k, v in m.items() %}
        {{ k }}: {{ v }}
      {{ k }}: {{ v }}
      {%- endfor %}
{% endfor %}
{%- endfor %}
{%- endfor %}
{%- endif %}
{%- endif %}
</syntaxhighlight>
</syntaxhighlight>


==Rendering Embedded Maps==
=Recursive Rendering of a Deep Data Structure to YAML=
{{Internal|Jinja2 Recursive Rendering of a Deep Data Structure to YAML#Overview|Recursive Rendering of a Deep Data Structure to YAML}}


If a complex recursive data structure, such as embedded maps, is passed to <code>render()</code>, an arbitrary level key can be accessed with the dot notation and also by key:
=Expressions=
Jinja allows expressions with a syntax similar to Python's. The expressions can be used in [[#Conditionals|conditionals]].
==Literals==
Jinja allows for the the following literals:
* Double quoted strings: <code>"Blue is a color"</code>.
* Integers: <code>34</code>.
* Floating points: <code>34.1</code>, <code>43.1e3</code>.
* Lists: <code>['this', 'is', 'a', 'list']</code>. Lists are useful for storing sequential data to be iterated over.
* Tuples: <code>('this', 'is', 'a', 'tuple')</code>.
* Dictionaries: <code>{'color': 'blue, 'size': 1}</code>
* Booleans: <code>true</code>, <code>false</code>. They are '''lowercase''', but <code>True</code> and <code>False</code> is also supported, but the documentation recommends using lowercase.
* None: <code>none</code>. It is '''lowercase'''. <code>None</code> is also supported, but the documentation recommends using lowercase.
==Math==
Supported operators:
* <code>+</code>: adds objects together. If the objets are numbers, performs mathematical addition. If the objects are strings and list, the operator concatenates the operands.
* <code>-</code>
* <code>/</code>
* <code>//</code>: divide numbers and returns the truncated result.
* <code>%</code>
* <code>*</code>: multiplies numbers, but can also be used to repeat a string a number of times:
<syntaxhighlight lang='jinja'>
{{ '-' * 80 }}
</syntaxhighlight>
==Comparisons==
===<tt>==</tt>===
Use <code>==</code> compare two objects and assess equality.
<syntaxhighlight lang='jinja'>
{%- if color == 'blue' -%}
Painted blue
{%- endif -%}
</syntaxhighlight>
 
<code>==</code> can be used to check for a specific boolean value:
<syntaxhighlight lang='jinja'>
{%if color_enabled == false  -%}
the color is enabled
{%- endif %}
</syntaxhighlight>
===<tt>!=</tt>===
Use <code>!=</code> compare two objects and assess inequality.
<syntaxhighlight lang='jinja'>
{%- if color != 'blue' -%}
not painted blue
{%- endif -%}
</syntaxhighlight>
Use <code>!=</code> with caution when attempting to detect empty strings. The following example:
<syntaxhighlight lang='jinja'>
{% if color != "" -%}
painted
{%- endif -%}
</syntaxhighlight>
will render "painted" if <code>color</code> has some value, or if <code>color</code> is explicitly assigned the empty string. If the <code>color</code> variable is not defined, or it is <code>None</code>, the template will render "painted", which is probably not intended. For a comprehensive approach on handling undefined, <code>None</code> and empty variables, see [[#Handling_Undefined.2C_None_and_Empty_Variables|Handling Undefined, None and Empty Variables]] below.
===Other Comparisons===
* <code>></code>
* <code>>=</code>
* <code><</code>
* <code><=</code>
==<tt>is</tt>==
<code>is</code>
===<tt>is defined</tt> Conditional===
The <code>is defined</code> expression evaluates to <code>true</code> or <code>false</code> whether the variable it was applied to was defined (exists) or not. For more details on usage see [[#Handling_Undefined.2C_None_and_Empty_Variables|Handling Undefined, None and Empty Variables]] below.
===Type Conditional===
A [[#Built-in_Tests|built-in test]] that checks the type may follow <code>is</code>:
To check whether a value is a string:
<syntaxhighlight lang='jinja'>
{%if some_var is string  -%}
...
</syntaxhighlight>
To check whether a value is a dictionary:
<syntaxhighlight lang='jinja'>
{%if some_var is mapping  -%}
...
</syntaxhighlight>
To check whether a value is a iterable:
<syntaxhighlight lang='jinja'>
{%if some_var is iterable  -%}
...
</syntaxhighlight>
 
==<tt>in</tt>==
<code>is</code>
==<tt>and</tt>, <tt>or</tt>, <tt>not</tt>==
* <code>and</code> return <code>true</code> if the left hand and the right hand operands are <code>true</code>.
* <code>or</code> return <code>true</code> if the left hand or the right hand operands are <code>true</code>.
* <code>not</code>
 
<syntaxhighlight lang='jinja'>
{%if some_var == "a" and some_other_var == "b" -%}
my vars are {{ some_var }}, {{ some_other_var }}
{%- endif %}
</syntaxhighlight>
 
==<tt>(expr)</tt>==
<code>(expr)</code> Groups an expression.
==<tt>|</tt>==
<code>|</code> apply a filter.
==<tt>~</tt>==
<code>~</code> convert all operands into strings and concatenate them.
==<tt>()</tt>==
<code>()</code> call a callable. Inside of the parentheses you can use positional arguments and keyword arguments Python-style.
==<tt>.</tt>, <tt>[]</tt>==
* <code>.</code> get an attribute of an object.
* <code>[]</code> index or key operator.
For a complex recursive data structure, such as recursively embedded maps, passed to <code>render()</code>, an arbitrary level key can be accessed with the dot notation and also by key. The template:
<syntaxhighlight lang='jinja'>
color1: {{ m.m1.color }}
color2: {{ m['m2']['color'] }}
</syntaxhighlight>
rendered with:
<syntaxhighlight lang='py'>
<syntaxhighlight lang='py'>
m = {
m = {
Line 201: Line 288:
     }
     }
}
}
template_as_text = '''
color1: {{ m.m1.color }}
color2: {{ m['m2']['color'] }}
'''
rtemplate = Environment().from_string(template_as_text)
data = rtemplate.render(m=m)
print(data)
</syntaxhighlight>
</syntaxhighlight>
will print:
will print:
Line 215: Line 294:
color2: blue
color2: blue
</syntaxhighlight>
</syntaxhighlight>
==Python Methods==
=Built-in Tests=
{{External|https://jinja.palletsprojects.com/en/latest/templates/#list-of-builtin-tests}}
{{External|https://www.webforefront.com/django/usebuiltinjinjafilters.html}}
===<tt>none()</tt>===
See [[#Handling_Undefined.2C_None_and_Empty_Variables|Handling Undefined, None and Empty Variables]] below.
===<tt>string()</tt>===
===<tt>number()</tt>===
===<tt>boolean()</tt>===
<syntaxhighlight lang='jinja'>
{% if some_var is boolean %} ...
</syntaxhighlight>
===<tt>mapping()</tt>===
===<tt>iterable()</tt>===


==Rendering an Object's __str__() Representation==
=Rendering an Object's __str__() Representation=


The <code>__str__()</code> is honored, but if it is a multi-line string, only the first line is correctly indented.
The <code>__str__()</code> is honored, but if it is a multi-line string, only the first line is correctly indented.


==Whitespace Management==
=Whitespace Management=
A leading dash removes all whitespace (including new lines) between the last non space template character and it. A trailing dash removes all whitespace (including new lines) between it and the next non-whitespace character from the template.
A leading dash removes all whitespace (including new lines) between the last non space template character and it. A trailing dash removes all whitespace (including new lines) between it and the next non-whitespace character from the template.
<syntaxhighlight lang='text'>
<syntaxhighlight lang='text'>
Line 234: Line 328:
  -%}
  -%}
</syntaxhighlight>
</syntaxhighlight>
===Indentation===
==Indentation==
<syntaxhighlight lang='text'>
<syntaxhighlight lang='text'>
   some_tag:
   some_tag:
Line 242: Line 336:


=Filters=
=Filters=
{{Internal|Jinja_Filters|Filters}}
{{External|https://jinja.palletsprojects.com/en/latest/templates/#list-of-builtin-filters}}
==<tt>upper</tt>==
<syntaxhighlight lang='groovy'>
"{{ ... | upper }}"
</syntaxhighlight>
==<tt>lower</tt>==
<syntaxhighlight lang='groovy'>
"{{ ... | lower }}"
</syntaxhighlight>
==<tt>indent</tt>==
{{External | https://jinja.palletsprojects.com/en/latest/templates/#jinja-filters.indent}}
<syntaxhighlight lang='jinja'>
some_tag:
    {{ rendered_multiline_configuration | indent(4) }}
</syntaxhighlight>
The first line must be explicitly indented with the same number of space. The spaces are added after each new line.
 
=Code Examples=
==Handling Undefined, None and Empty Variables==
 
The expression to check whether a variable has been defined or not is <code>is defined</code>. If we want to strictly test whether the variable is defined, even if its value is <code>None</code> or non-truthy, use <code>is defined</code>:
 
<syntaxhighlight lang='jinja'>
{%if color is defined  -%}
color: {{ color }} (may display None or a non-truthy value)
{%- else -%}
color variable is not defined
{%- endif %}
</syntaxhighlight>
 
Simply using the variable as test expression, Python-style, will evaluate to <code>false</code> for non-defined variables, but also for variables that have been defined and have a <code>None</code> or a non-truthy value.
 
<syntaxhighlight lang='jinja'>
{%if color  -%}
color: {{ color }}
{%- else -%}
color variable is not defined, or it is None, or it is non-truthy
{%- endif %}
</syntaxhighlight>
 
It is a good idea to check whether a variable exists with <code>if <var-name></code> or <code>if <var-name> is defined</code> because an attempt to use an undefined variable '''in an expression''' will raise an exception. It is fine to use undefined variable in a rendering expression, the variable will render as the empty string.
 
<font color=darkkhaki>The documentation says that <code>[[Jinja2#none.28.29|none(var)]]</code> expression identifies <code>None</code> values, but the execution of the following code with Jinja2 3.1.2 raised <code>TypeError: 'NoneType' object is not callable</code> regardless whether I defined or not <code>color</code> variable when rendering:
 
<syntaxhighlight lang='jinja'>
{%if none(color)  -%}
NONE
{%- endif %}
</syntaxhighlight>
</font>
==Recursive Rendering of a Deep Data Structure to YAML==
{{Internal|Jinja2 Recursive Rendering of a Deep Data Structure to YAML#Overview|Recursive Rendering of a Deep Data Structure to YAML}}

Latest revision as of 18:56, 12 September 2023

External

Internal

Overview

Jinja2 is a templating engine. Placeholders in the template allow writing code that has a syntax similar to Python. The engine ingests the template and data and renders the final document. Jinja2 provides template inheritance and inclusion. Jinja2 allows definition and import of macros within the template. Templated are compiled to Python code and cached, or they can be compiled ahead of time. Jinja2 provides extensible filters, tests, functions and syntax.

Jinja’s philosophy is that while application logic belongs in Python if possible, it shouldn’t make the template designer’s job difficult by restricting functionality too much.

Programming Model

Find out what the latest version is from https://pypi.org/project/Jinja2/

Then add this to your requirements.txt

jinja2 == 3.1.2

Read the Template from File

from pathlib import Path
from jinja2 import Environment, FileSystemLoader

tdir = Path('/Users/ovidiu/tmp/jinja')
template = Environment(loader=FileSystemLoader(tdir)).get_template('test-template.yaml.j2')

text = template.render(
    variable_1='blue',
    variable_2=5
)

For a template similar to:

color: {{  variable_1 }}
size: {{  variable_2 }}

the rendering looks similar to:

color: blue
size: 5

Template as String in Memory

template_as_text = """
color: {{  variable_1 }}
size: {{  variable_2 }}
"""

template = Environment().from_string(template_as_text)

text = template.render(
    variable_1='blue',
    variable_2=5
)

Variables

Variables can be provided individually as arguments of the render() method, as shown above.

Alternatively, a recursive data structure can be provided as argument:

data = {
    'variable_1': 'blue',
    'variable_2': 5,
}
text = template.render(data)

Template Variable

raw content followed by {{ color }}

If the color variable in the example above is not defined when the template is rendered, it will be rendered as an empty string.

Also see Variables above.

Conditionals

https://jinja.palletsprojects.com/en/latest/templates/#if-expression

The expressions described below can be used in conditionals:

{% if <expression> %}
something will be rendered here
{% elif <expression>  %}
something else will be rendered here
{% else %}
and something else will be rendered here
{% endif %}

Example:

{%- if color == 'blue' -%}
BLUE
{%- elif color == 'red'  -%}
RED
{%- else -%}
UNDEFINED
{%- endif -%}

Loops

my_list:
{%- for i in seq %}
  - '{{ i }}'
{%- endfor %}

where seq is a Python list:

text = Environment(...).get_template(...).render(seq=['a', 'b'])

Macros

https://jinja.palletsprojects.com/en/latest/templates/#macros

Macros are equivalent with functions in a regular programming language:

{%- macro some_macro(color, size) -%}
color: {{ color }}, size: {{ size }}
{%- endmacro -%}

{{- some_macro('blue', 1) }}

Rendering Booleans

If a boolean value from Python will be rendered as having its first character capitalized. This is a problem when rendering YAML or JSON. This is something that works but I am not sure is optimal (need to adjust whitespace):

{% if value is boolean %}{{ value | lower }}
{% else %}{{ value }}
{% endif %}

Rendering Dictionaries

something:
{%- for key, value in a_dict.items() %}
    {{key}}: {{value-}} 
{% endfor %}
somethingelse
text = Environment().from_string(...).render(a_dict={'a': 'A', 'b': 'B', 'c': 'C'})

This will render:

something:
    a: A
    b: B
    c: C
somethingelse

Rendering a List of Dictionaries

{% if a_list_of_maps %}
  some_tag_that_introduces_the_list_of_maps:
{%- for m in a_list_of_maps %}
     - {%- for k, v in m.items() %}
         {{ k }}: {{ v }}
       {%- endfor %}
{%- endfor %}
{%- endif %}

Recursive Rendering of a Deep Data Structure to YAML

Recursive Rendering of a Deep Data Structure to YAML

Expressions

Jinja allows expressions with a syntax similar to Python's. The expressions can be used in conditionals.

Literals

Jinja allows for the the following literals:

  • Double quoted strings: "Blue is a color".
  • Integers: 34.
  • Floating points: 34.1, 43.1e3.
  • Lists: ['this', 'is', 'a', 'list']. Lists are useful for storing sequential data to be iterated over.
  • Tuples: ('this', 'is', 'a', 'tuple').
  • Dictionaries: {'color': 'blue, 'size': 1}
  • Booleans: true, false. They are lowercase, but True and False is also supported, but the documentation recommends using lowercase.
  • None: none. It is lowercase. None is also supported, but the documentation recommends using lowercase.

Math

Supported operators:

  • +: adds objects together. If the objets are numbers, performs mathematical addition. If the objects are strings and list, the operator concatenates the operands.
  • -
  • /
  • //: divide numbers and returns the truncated result.
  • %
  • *: multiplies numbers, but can also be used to repeat a string a number of times:
{{ '-' * 80 }}

Comparisons

==

Use == compare two objects and assess equality.

{%- if color == 'blue' -%}
Painted blue
{%- endif -%}

== can be used to check for a specific boolean value:

{%if color_enabled == false  -%}
the color is enabled
{%- endif %}

!=

Use != compare two objects and assess inequality.

{%- if color != 'blue' -%}
not painted blue
{%- endif -%}

Use != with caution when attempting to detect empty strings. The following example:

{% if color != "" -%}
painted
{%- endif -%}

will render "painted" if color has some value, or if color is explicitly assigned the empty string. If the color variable is not defined, or it is None, the template will render "painted", which is probably not intended. For a comprehensive approach on handling undefined, None and empty variables, see Handling Undefined, None and Empty Variables below.

Other Comparisons

  • >
  • >=
  • <
  • <=

is

is

is defined Conditional

The is defined expression evaluates to true or false whether the variable it was applied to was defined (exists) or not. For more details on usage see Handling Undefined, None and Empty Variables below.

Type Conditional

A built-in test that checks the type may follow is: To check whether a value is a string:

{%if some_var is string  -%}
 ...

To check whether a value is a dictionary:

{%if some_var is mapping  -%}
 ...

To check whether a value is a iterable:

{%if some_var is iterable  -%}
 ...

in

is

and, or, not

  • and return true if the left hand and the right hand operands are true.
  • or return true if the left hand or the right hand operands are true.
  • not
{%if some_var == "a" and some_other_var == "b" -%}
my vars are {{ some_var }}, {{ some_other_var }}
{%- endif %}

(expr)

(expr) Groups an expression.

|

| apply a filter.

~

~ convert all operands into strings and concatenate them.

()

() call a callable. Inside of the parentheses you can use positional arguments and keyword arguments Python-style.

., []

  • . get an attribute of an object.
  • [] index or key operator.

For a complex recursive data structure, such as recursively embedded maps, passed to render(), an arbitrary level key can be accessed with the dot notation and also by key. The template:

color1: {{ m.m1.color }}
color2: {{ m['m2']['color'] }}

rendered with:

m = {
    'm1': {
        'color': 'red'
    },
    'm2': {
        'color': 'blue'
    }
}

will print:

color1: red
color2: blue

Python Methods

Built-in Tests

https://jinja.palletsprojects.com/en/latest/templates/#list-of-builtin-tests
https://www.webforefront.com/django/usebuiltinjinjafilters.html

none()

See Handling Undefined, None and Empty Variables below.

string()

number()

boolean()

{% if some_var is boolean %} ...

mapping()

iterable()

Rendering an Object's __str__() Representation

The __str__() is honored, but if it is a multi-line string, only the first line is correctly indented.

Whitespace Management

A leading dash removes all whitespace (including new lines) between the last non space template character and it. A trailing dash removes all whitespace (including new lines) between it and the next non-whitespace character from the template.

 {{-
 -}}
 {%-
 -%}

Indentation

  some_tag:
    {{ rendered_multiline_configuration | indent(4) }}

The first line must be explicitly indented with the same number of space. The spaces are added after each new line.

Filters

https://jinja.palletsprojects.com/en/latest/templates/#list-of-builtin-filters

upper

"{{ ... | upper }}"

lower

"{{ ... | lower }}"

indent

https://jinja.palletsprojects.com/en/latest/templates/#jinja-filters.indent
some_tag:
    {{ rendered_multiline_configuration | indent(4) }}

The first line must be explicitly indented with the same number of space. The spaces are added after each new line.

Code Examples

Handling Undefined, None and Empty Variables

The expression to check whether a variable has been defined or not is is defined. If we want to strictly test whether the variable is defined, even if its value is None or non-truthy, use is defined:

{%if color is defined  -%}
color: {{ color }} (may display None or a non-truthy value)
{%- else -%}
color variable is not defined
{%- endif %}

Simply using the variable as test expression, Python-style, will evaluate to false for non-defined variables, but also for variables that have been defined and have a None or a non-truthy value.

{%if color  -%}
color: {{ color }}
{%- else -%}
color variable is not defined, or it is None, or it is non-truthy
{%- endif %}

It is a good idea to check whether a variable exists with if <var-name> or if <var-name> is defined because an attempt to use an undefined variable in an expression will raise an exception. It is fine to use undefined variable in a rendering expression, the variable will render as the empty string.

The documentation says that none(var) expression identifies None values, but the execution of the following code with Jinja2 3.1.2 raised TypeError: 'NoneType' object is not callable regardless whether I defined or not color variable when rendering:

{%if none(color)  -%}
NONE
{%- endif %}

Recursive Rendering of a Deep Data Structure to YAML

Recursive Rendering of a Deep Data Structure to YAML