Jinja2

From NovaOrdis Knowledge Base
Jump to navigation Jump to search

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

Once the virtual environment is updated, use it as such:

from pathlib import Path
from jinja2 import Environment, FileSystemLoader

template_dir = Path('...')
text = Environment(loader=FileSystemLoader(template_dir))\
  .get_template('my-template.yaml.j2') \
  .render(variable_1='some value', variable_2='some other value')

If the content is already loaded:

template_as_text = '...'
rtemplate = Environment().from_string(template_as_text)
data = rtemplate.render(variable_1='some value', variable_2='some other value')

Variables

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

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

Templating Language

Variable

raw content followed by {{ variable1 }}

Conditional

{%if some_var == "something" -%}
something
something else {{ var_1 }}
{% elif some_var == "something else" -%}
something else
{% else %}
something completely different {{ var_2 }}
{%- endif %}

This works with empty strings too:

{%if some_var != "" -%}
my var is {{ some_var }}

defined Conditional

To check whether a variable is defined or not defined:

{%if some_var is defined  -%}
something
{%- endif %}
{%if some_var is not defined  -%}
something else
{%- endif %}

Check whether a Value is None

Note that a variable that has a None value is still defined, so the above is defined test will pass.

To check for None, specifically:

{%if none(some_var)  -%}
'som_var' is None
{%- endif %}

The following construct broke the renderer when some_var was None:

{%if some_var is some_var and not none(some_var) -%}
{{ queues }}
{%- endif %}

Error message:

>   ??? 
E   TypeError: 'NoneType' object is not callable

Specific Boolean Values

To check for a specific boolean value:

{%if some_var == false  -%}
something
{%- endif %}

Type Conditional

To check whether a value is a string:

{%if some_var is string  -%}
something
{%- endif %}

For more built-in filters and tests:

https://www.webforefront.com/django/usebuiltinjinjafilters.html

Conditional Expressions

{%if some_var == "a" and some_other_var == "b" -%}
my vars are {{ some_var }}, {{ some_other_var }}

Loops

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

where seq is a Python list:

seq = ['a', 'b']
text = Environment(loader=FileSystemLoader(template_dir)).get_template('my-template.yaml.j2').render(seq=seq)

Rendering Dictionaries

from jinja2 import Environment

template_as_text = '''
something:
{%- for key, value in a_dict.items() %}
    {{key}}: {{value-}} 
{% endfor %}
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)

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

Rendering Embedded Maps

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

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

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)

will print:

color1: red
color2: blue

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

Filters

TODO

What happens if a document containing a variable is rendered and the variable value is not provided?