knowledge/technology/tools/Jinja.md

17 KiB
Raw Permalink Blame History

obj wiki website rev
concept https://en.wikipedia.org/wiki/Jinja_(template_engine) https://jinja.palletsprojects.com/en/3.1.x 2024-02-19

Jinja Templates

Jinja is a fast, expressive, extensible templating engine. Special placeholders in the template allow writing code similar to Python syntax. Then the template is passed data to render the final document. Jinja template files commonly have the .j2 extension.

Syntax

Below is a minimal template that illustrates a few basics using the default Jinja configuration:

<!DOCTYPE html>
<html lang="en">
<head>
    <title>My Webpage</title>
</head>
<body>
    <ul id="navigation">
    {% for item in navigation %}
        <li><a href="{{ item.href }}">{{ item.caption }}</a></li>
    {% endfor %}
    </ul>

    <h1>My Webpage</h1>
    {{ a_variable }}

    {# a comment #}
</body>
</html>

There are a few kinds of delimiters. The default Jinja delimiters are configured as follows:

  • {% ... %} for Statements
  • {{ ... }} for Expressions to print to the template output
  • {# ... #} for Comments not included in the template output

Comments

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:

{# note: commented-out template because we no longer use this
    {% for user in users %}
        ...
    {% endfor %}
#}

Expressions

Jinja allows basic expressions everywhere. These work very similarly to regular Python; even if youre not working with Python you should feel comfortable with it.

Literals

The simplest form of expressions are literals. Literals are representations for Python objects such as strings and numbers. The following literals exist:

"Hello World"

Everything between two double or single quotes is a string. They are useful whenever you need a string in the template (e.g. as arguments to function calls and filters, or just to extend or include a template).

42 / 123_456

Integers are whole numbers without a decimal part. The _ character can be used to separate groups for legibility.

42.23 / 42.1e2 / 123_456.789

Floating point numbers can be written using a . as a decimal mark. They can also be written in scientific notation with an upper or lower case e to indicate the exponent part. The _ character can be used to separate groups for legibility, but cannot be used in the exponent part.

['list', 'of', 'objects']

Everything between two brackets is a list. Lists are useful for storing sequential data to be iterated over. For example, you can easily create a list of links using lists and tuples for (and with) a for loop:

<ul>
{% for href, caption in [('index.html', 'Index'), ('about.html', 'About'),
                         ('downloads.html', 'Downloads')] %}
    <li><a href="{{ href }}">{{ caption }}</a></li>
{% endfor %}
</ul>

('tuple', 'of', 'values')

Tuples are like lists that cannot be modified (“immutable”). If a tuple only has one item, it must be followed by a comma (('1-tuple',)). Tuples are usually used to represent items of two or more elements. See the list example above for more details.

{'dict': 'of', 'key': 'and', 'value': 'pairs'}

A dict in Python is a structure that combines keys and values. Keys must be unique and always have exactly one value. Dicts are rarely used in templates; they are useful in some rare cases such as the xmlattr() filter.

true / false

true is always true and false is always false.

If Expression

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:

{% extends layout_template if layout_template is defined else 'default.html' %}

The general syntax is <do something> if <something is true> else <do something else>.

The else part is optional. If not provided, the else block implicitly evaluates into an Undefined object (regardless of what undefined in the environment is set to):

{{ "[{}]".format(page.title) if page.title }}

Builtin Filters

batch(value, linecount: int, fill_with: 'Optional' = None)

A filter that batches items. It works pretty much like slice just the other way round. It returns a list of lists with the given number of items. If you provide a second parameter this is used to fill up missing items. See this example:

<table>
{%- for row in items|batch(3, '&nbsp;') %}
  <tr>
  {%- for column in row %}
    <td>{{ column }}</td>
  {%- endfor %}
  </tr>
{%- endfor %}
</table>
center(value, width: int = 80)

Centers the value in a field of a given width.

default(value, default_value, boolean: bool = False)

If the value is undefined it will return the passed default value, otherwise the value of the variable:

{{ my_variable|default('my_variable is not defined') }}

This will output the value of my_variable if the variable was defined, otherwise 'my_variable is not defined'. If you want to use default with variables that evaluate to false you have to set the second parameter to true:

{{ ''|default('the string was empty', true) }}
dictsort(value, case_sensitive: bool = False, by: Literal["key", "value"]' = 'key', reverse: bool = False)

Sort a dict and yield (key, value) pairs. Python dicts may not be in the order you want to display them in, so sort them first.

{% for key, value in mydict|dictsort %}
    sort the dict by key, case insensitive

{% for key, value in mydict|dictsort(reverse=true) %}
    sort the dict by key, case insensitive, reverse order

{% for key, value in mydict|dictsort(true) %}
    sort the dict by key, case sensitive

{% for key, value in mydict|dictsort(false, 'value') %}
    sort the dict by value, case insensitive
escape(value)

Replace the characters &, <, >, ', and " in the string with HTML-safe sequences. Use this if you need to display text that might contain such characters in HTML.

If the object has an __html__ method, it is called and the return value is assumed to already be safe for HTML.

filesizeformat(value, binary: bool = False)

Format the value like a human-readable file size (i.e. 13 kB, 4.1 MB, 102 Bytes, etc). Per default decimal prefixes are used (Mega, Giga, etc.), if the second parameter is set to True the binary prefixes are used (Mebi, Gibi).

first(seq)

Return the first item of a sequence.

float(value, default: float = 0.0)

Convert the value into a floating point number. If the conversion doesnt work it will return 0.0. You can override this default using the first parameter.

forceescape(value)

Enforce HTML escaping. This will probably double escape variables.

format(value, args...)

Apply the given values to a printf-style format string, like string % values.

{{ "%s, %s!"|format(greeting, name) }}
Hello, World!

In most cases it should be more convenient and efficient to use the % operator or str.format().

{{ "%s, %s!" % (greeting, name) }}
{{ "{}, {}!".format(greeting, name) }}
groupby(value, attribute: str | int, default: Any | None = None, case_sensitive: bool = False)

Group a sequence of objects by an attribute using Pythons itertools.groupby(). The attribute can use dot notation for nested access, like "address.city". Unlike Pythons groupby, the values are sorted first so only one group is returned for each unique value.

For example, a list of User objects with a city attribute can be rendered in groups. In this example, grouper refers to the city value of the group.

<ul>{% for city, items in users|groupby("city") %}
  <li>{{ city }}
    <ul>{% for user in items %}
      <li>{{ user.name }}
    {% endfor %}</ul>
  </li>
{% endfor %}</ul>

groupby yields namedtuples of (grouper, list), which can be used instead of the tuple unpacking above. grouper is the value of the attribute, and list is the items with that value.

<ul>{% for group in users|groupby("city") %}
  <li>{{ group.grouper }}: {{ group.list|join(", ") }}
{% endfor %}</ul>

You can specify a default value to use if an object in the list does not have the given attribute.

<ul>{% for city, items in users|groupby("city", default="NY") %}
  <li>{{ city }}: {{ items|map(attribute="name")|join(", ") }}</li>
{% endfor %}</ul>
indent(s: str, width: int | str = 4, first: bool = False, blank: bool = False)

Return a copy of the string with each line indented by 4 spaces. The first line and blank lines are not indented by default.

Parameters:

  • width Number of spaces, or a string, to indent by.
  • first Dont skip indenting the first line.
  • blank Dont skip indenting empty lines.
items(value)

Return an iterator over the (key, value) items of a mapping.

x|items is the same as x.items(), except if x is undefined an empty iterator is returned.

This filter is useful if you expect the template to be rendered with an implementation of Jinja in another programming language that does not have a .items() method on its mapping type.

<dl>
{% for key, value in my_dict|items %}
    <dt>{{ key }}
    <dd>{{ value }}
{% endfor %}
</dl>
join(value, d: str = '', attribute: str | int | NoneType = None)

Return a string which is the concatenation of the strings in the sequence. The separator between elements is an empty string per default, you can define it with the optional parameter:

{{ [1, 2, 3]|join('|') }}
    -> 1|2|3

{{ [1, 2, 3]|join }}
    -> 123
last(seq)

Return the last item of a sequence.

length(obj)

Return the number of items in a container.

max(value)

Return the largest item from the sequence.

{{ [1, 2, 3]|max }}
    -> 3
min(value)

Return the smallest item from the sequence.

{{ [1, 2, 3]|min }}
    -> 1
random(seq)

Return a random item from the sequence.

replace(s: str, old: str, new: str, count: int | None = None)

Return a copy of the value with all occurrences of a substring replaced with a new one. The first argument is the substring that should be replaced, the second is the replacement string. If the optional third argument count is given, only the first count occurrences are replaced:

{{ "Hello World"|replace("Hello", "Goodbye") }}
    -> Goodbye World

{{ "aaaaargh"|replace("a", "d'oh, ", 2) }}
    -> d'oh, d'oh, aaargh
reverse(value)

Reverse the object or return an iterator that iterates over it the other way round.

round(value: float, precision: int = 0, method: Literal["common", "ceil", "floor"]' = 'common')

Round the number to a given precision. The first parameter specifies the precision (default is 0), the second the rounding method:

  • 'common' rounds either up or down
  • 'ceil' always rounds up
  • 'floor' always rounds down

If you dont specify a method 'common' is used.

{{ 42.55|round }}
    -> 43.0
{{ 42.55|round(1, 'floor') }}
    -> 42.5

Note that even if rounded to 0 precision, a float is returned. If you need a real integer, pipe it through int:

{{ 42.55|round|int }}
    -> 43
trim(value)

Strip leading and trailing characters, by default whitespace.

truncate(s: str, length: int = 255, killwords: bool = False, end: str = '...', leeway: int | None = None)

Return a truncated copy of the string. The length is specified with the first parameter which defaults to 255. If the second parameter is true the filter will cut the text at length. Otherwise it will discard the last word. If the text was in fact truncated it will append an ellipsis sign ("..."). If you want a different ellipsis sign than "..." you can specify it using the third parameter. Strings that only exceed the length by the tolerance margin given in the fourth parameter will not be truncated.

{{ "foo bar baz qux"|truncate(9) }}
    -> "foo..."
{{ "foo bar baz qux"|truncate(9, True) }}
    -> "foo ba..."
{{ "foo bar baz qux"|truncate(11) }}
    -> "foo bar baz qux"
{{ "foo bar baz qux"|truncate(11, False, '...', 0) }}
    -> "foo bar..."
unique(value, case_sensitive: bool = False, attribute: str | int | NoneType = None)

Returns a list of unique items from the given iterable.

{{ ['foo', 'bar', 'foobar', 'FooBar']|unique|list }}
    -> ['foo', 'bar', 'foobar']

The unique items are yielded in the same order as their first occurrence in the iterable passed to the filter.

Parameters:

  • case_sensitive Treat upper and lower case strings as distinct.
  • attribute Filter objects with unique values for this attribute.
urlencode(value)

Quote data for use in a URL path or query using UTF-8.

Basic wrapper around urllib.parse.quote() when given a string, or urllib.parse.urlencode() for a dict or iterable.

urlize(value)

Convert URLs in text into clickable links.

This may not recognize links in some situations. Usually, a more comprehensive formatter, such as a Markdown library, is a better choice.

wordcount(s: str)

Count the words in that string.

wordwrap(s: str, width: int = 79, break_long_words: bool = True, wrapstring: str | None = None, break_on_hyphens: bool = True)

Wrap a string to the given width. Existing newlines are treated as paragraphs to be wrapped separately.

Parameters:

  • s Original text to wrap.
  • width Maximum length of wrapped lines.
  • break_long_words If a word is longer than width, break it across lines.
  • break_on_hyphens If a word contains hyphens, it may be split across lines.
  • wrapstring String to join each wrapped line. Defaults to Environment.newline_sequence.

Builtin Tests

boolean(value)

Return true if the object is a boolean value.

defined(value)

Return true if the variable is defined:

{% if variable is defined %}
    value of variable: {{ variable }}
{% else %}
    variable is not defined
{% endif %}

See the default() filter for a simple way to set undefined variables.

divisibleby(value, num: int)

Check if a variable is divisible by a number.

even(value)

Return true if the variable is even.

in(value, seq)

Check if value is in seq.

odd(value)

Return true if the variable is odd.

Statements

for

Loop over each item in a sequence. For example, to display a list of users provided in a variable called users:

<h1>Members</h1>
<ul>
{% for user in users %}
  <li>{{ user.username }}</li>
{% endfor %}
</ul>

Inside of a for-loop block, you can access some special 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 first iteration.
loop.last True if last iteration.
loop.length The number of items in the sequence.
loop.cycle A helper function to cycle between a list of sequences. See the explanation below.
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 of the loop. Undefined during the first iteration.
loop.nextitem The item from the following iteration of the loop. Undefined during the last iteration.
loop.changed(*val) True if previously called with a different value (or not called at all).

Within a for-loop, its possible to cycle among a list of strings/variables each time through the loop by using the special loop.cycle helper:

{% for row in rows %}
    <li class="{{ loop.cycle('odd', 'even') }}">{{ row }}</li>
{% endfor %}

if

The if statement in Jinja is comparable with the Python if statement. In the simplest form, you can use it to test if a variable is defined, not empty and not false:

{% if kenny.sick %}
    Kenny is sick.
{% elif kenny.dead %}
    You killed Kenny!  You bastard!!!
{% else %}
    Kenny looks okay --- so far
{% endif %}

include

The include tag renders another template and outputs the result into the current template.

{% include 'header.html' %}
Body goes here.
{% include 'footer.html' %}