Jinja2
Формат Jinja2
Переменные шаблона Jinja могут относиться к любому типу или объекту Python, если их можно преобразовать в строки. Тип модели, списка или словаря можно передать шаблону и отобразить его атрибуты, поместив эти атрибуты во второй блок, указанный ранее.
Виды синтаксиса
{% … %} управляющие структуры
{{ todo.item }} вывод значений переданных ему выражений {# This is a great API book! #} комментарии
Иерархия шаблонов
Способ
Описание
{% include %}
Позволяет включить содержимое другого шаблона целиком.
{% include 'partials/footer.html' %}
{% endblock %}
Фильтры
{{ variable | filter_name(*args) }}
Виды фильтров
Название
Описание
default(strdefault)
Замена вывода переданного значения, если оно оказывается None
{{ todo.item | default('This is a default todo item') }}
escape
Отображение необработанного вывода HTML
striptags
Удаление HTML тетов перед отправкой
int
float
Преобразование типов перед ответом
{{ 3.142 | int }}
3
{{ 31 | float }}
31.0
join(whitespace)
Объединение элементов списка в строку
{{ ['Packt', 'produces', 'great', 'books!'] | join(' ') }}
Packt produces great books!
length
Длина переданного объекта
Todo count: {{ todos | length }}
Todo count: 4
Полный список фильтров
Условия:
{% if user %}
Hello, {{ user.name }}!
{% else %}
Hello, Unknown!
{% endif %}
Циклы
{% for comment in comments %}
{{ comment }}
{% endfor %}
Можно также обратиться к переменной loop.index для получения дополнительной информации
Переменная
Описание
loop.index
Текущее значение итерации (1 - первая итерация)
loop.index0
Текущее значение итерации (0 - первая итерация)
loop.revindex loop.revindex0
Кол-во оставшихся итераций
loop.first
True если первая итерация
loop.last
loop.length
loop.pervitem loop.nextitem
Значение предыдущей/следующей итерации (пусто если не существует)
Макросы:
{% macro render_comment(comment) %}
{{ comment }}
{% endmacro %}
{% for comment in comments %}
{{ render_comment(comment) }}
{% endfor %}
Макросы можно импортировать из файлов
{% import 'macros.html' as macros %}
В Jinja двойные фигурные скобки {{ }} позволяют получить результат выражение, переменную или вызвать функцию и вывести значение в шаблоне.
class Foo:
def __str__(self):
return "This is an instance of Foo class"
Template("{{ var }}").render(var=Foo())
'This is an instance of Foo class'
Если обратится к индексу, который не существует, Jinja просто выведет пустую строку.
Вызов функции
В Jinja для определения функции ее нужно просто вызвать.
def foo():
return "foo() called"
Template("{{ foo() }}").render(foo=foo)
'foo() called'
Объявление переменных
Внутри шаблона можно задать переменную с помощью инструкции set.
{% set fruit = 'apple' %}
{% set name, age = 'Tom', 20 %}
Переменные определяются для хранения результатов сложных операций, так чтобы их можно было использовать дальше в шаблоне. Переменные, определенные вне управляющих конструкций (о них дальше), ведут себя как глобальные переменные и доступны внутри любой структуры. Тем не менее переменные, созданные внутри конструкций, ведут себя как локальные переменные и видимы только внутри этих конкретных конструкций. Единственное исключение — инструкция if.
Цикл и условные выражения
Реклама
Управляющие конструкции позволяют добавлять в шаблоны элементы управления потоком и циклы. По умолчанию, управляющие конструкции используют разделитель {% … %} вместо двойных фигурных скобок {{ ... }}.
Инструкция if
Инструкция if в Jinja имитирует выражение if в Python, а значение условия определяет набор инструкции. Например:
{% if bookmarks %}
User has some bookmarks
{% endif %}
Если значение переменной bookmarks – True, тогда будет выведена строка
User has some bookmarks
. Стоит запомнить, что в Jinja, если у переменной нет значения, она возвращает False.
Также можно использовать условия elif и else, как в обычном коде Python. Например:
{% if user.newbie %}
Display newbie stages
{% elif user.pro %}
Display pro stages
{% elif user.ninja %}
Display ninja stages
{% else %}
You have completed all stages
{% endif %}
Управляющие инструкции также могут быть вложенными. Например:
{% if user %}
{% if user.newbie %}
Display newbie stages
{% elif user.pro %}
Display pro stages
{% elif user.ninja %}
Display ninja stages
{% else %}
You have completed all states
{% endif %}
{% else %}
User is not defined
{% endif %}
Реклама
В определенных случаях достаточно удобно записывать инструкцию if в одну строку. Jinja поддерживает такой тип записи, но называет это выражением if, потому что оно записывается с помощью двойных фигурных скобок {{ … }}, а не {% … %}. Например:
{{ "User is logged in" if loggedin else "User is not logged in" }}
Здесь если переменная loggedin вернет True, тогда будет выведена строка “User is logged in”. В противном случае — “User is not logged in”.
Условие else использовать необязательно. Если его нет, тогда блок else вернет объект undefined.
{{ "User is logged in" if loggedin }}
Здесь, если переменная loggedin вернет True, будет выведена строка “User is logged in”. В противном случае — ничего.
Как и в Python можно использовать операторы сравнения, присваивания и логические операторы для управляющих конструкций, чтобы создавать более сложные условия. Вот несколько примеров:
{# Если user.count ревен 1000, код '
User count is 1000
' отобразится #}
{% if users.count == 1000 %}
User count is 1000
{% endif %}
{# Если выражение 10 >= 2 верно, код '
10 >= 2
' отобразится #}
{% if 10 >= 2 %}
10 >= 2
{% endif %}
{# Если выражение "car" <= "train" верно, код '
car <= train
' отобразится #}
{% if "car" <= "train" %}
car <= train
{% endif %}
{#
Если user залогинен и superuser, код
'
User is logged in and is a superuser
' отобразится
#}
{% if user.loggedin and user.is_superuser %}
User is logged in and is a superuser
{% endif %}
{#
Если user является superuser, moderator или author, код
'Edit' отобразится
#}
{% if user.is_superuser or user.is_moderator or user.is_author %}
Edit
{% endif %}
{#
Если user и current_user один и тот же объект, код
user and current_user are same
отобразится
#}
{% if user is current_user %}
user and current_user are same
{% endif %}
{#
Если "Flask" есть в списке, код
'
Flask is in the dictionary
' отобразится
#}
{% if ["Flask"] in ["Django", "web2py", "Flask"] %}
Flask is in the dictionary
{% endif %}
Если условия становятся слишком сложными, или просто есть желание поменять приоритет оператора, можно обернуть выражения скобками ():
{% if (user.marks > 80) and (user.marks < 90) %}
You grade is B
{% endif %}
Цикл for
Цикл for позволяет перебирать последовательность. Например:
{% set user_list = ['tom', 'jerry', 'spike'] %}
{% for user in user_list %}
{{ user }}
{% endfor %}
Вывод:
tom
jerry
spike
Вот как можно перебирать значения словаря:
{% set employee = { 'name': 'tom', 'age': 25, 'designation': 'Manager' } %}
{% for key in employee.items() %}
{{ key }} : {{ employee[key] }}
{% endfor %}
Вывод:
designation : Manager
name : tom
age : 25
Примечание: в Python элементы словаря не хранятся в конкретном порядке, поэтому вывод может отличаться.
Если нужно получить ключ и значение словаря вместе, используйте метод items().
{% set employee = { 'name': 'tom', 'age': 25, 'designation': 'Manager' } %}
{% for key, value in employee.items() %}
{{ key }} : {{ value }}
{% endfor %}
Вывод:
designation : Manager
name : tom
age : 25
Цикл for также может использовать дополнительное условие else, как в Python, но зачастую способ его применения отличается. Стоит вспомнить, что в Python, если else идет следом за циклом for, условие else выполняется только в том случае, если цикл завершается после перебора всей последовательности, или если она пуста. Оно не выполняется, если цикл остановить оператором break.
Когда условие else используется в цикле for в Jinja, оно исполняется только в том случае, если последовательность пустая или не определена. Например:
Реклама
{% set user_list = [] %}
{% for user in user_list %}
{{ user }}
{% else %}
user_list is empty
{% endfor %}
Вывод:
user_list is empty
По аналогии с вложенными инструкциями if, можно использовать вложенные циклы for. На самом деле, любые управляющие конструкции можно вкладывать одна в другую.
{% for user in user_list %}
{{ user.full_name }}
{% for follower in user.followers %}
{{ follower }}
{% endfor %}
{% endfor %}
Цикл for предоставляет специальную переменную loop для отслеживания прогресса цикла. Например:
{% for user in user_list %}
{{ loop.index }} - {{ user }}
{% endfor %}
loop.index внутри цикла for начинает отсчет с 1. В таблице упомянуты остальные широко используемые атрибуты переменной loop.
Метод Значение
loop.index0 то же самое что и loop.index, но с индексом 0, то есть, начинает считать с 0, а не с 1.
loop.revindex возвращает номер итерации с конца цикла (считает с 1).
loop.revindex0 возвращает номер итерации с конца цикла (считает с 0).
loop.first возвращает True, если итерация первая. В противном случае — False.
loop.last возвращает True, если итерация последняя. В противном случае — False.
loop.length возвращает длину цикла(количество итераций).
Примечание: полный список есть в документации Flask.
Фильтры
Фильтры изменяют переменные до процесса рендеринга. Синтаксис использования фильтров следующий:
variable_or_value|filter_name
Вот пример:
{{ comment|title }}
Фильтр title делает заглавной первую букву в каждом слове. Если значение переменной comment — "dust in the wind", то вывод будет "Dust In The Wind".
Можно использовать несколько фильтров, чтобы точнее настраивать вывод. Например:
{{ full_name|striptags|title }}
Фильтр striptags удалит из переменной все HTML-теги. В приведенном выше коде сначала будет применен фильтр striptags, а затем — title.
У некоторых фильтров есть аргументы. Чтобы передать их фильтру, нужно вызвать фильтр как функцию. Например:
{{ number|round(2) }}
Фильтр round округляет число до конкретного количества символов.
В следующей таблице указаны широко используемые фильтры.
Название Описание
upper делает все символы заглавными
lower приводит все символы к нижнему регистру
capitalize делает заглавной первую букву и приводит остальные к нижнему регистру
escape экранирует значение
safe предотвращает экранирование
length возвращает количество элементов в последовательности
trim удаляет пустые символы в начале и в конце
random возвращает случайный элемент последовательности
Примечание: полный список фильтров доступен здесь.
Макросы
Макросы в Jinja напоминают функции в Python. Суть в том, чтобы сделать код, который можно использовать повторно, просто присвоив ему название. Например:
{% macro render_posts(post_list, sep=False) %}
{% endmacro %}
В этом примере создан макрос render_posts, который принимает обязательный аргумент post_list и необязательный аргумент sep. Использовать его нужно следующим образом:
{{ render_posts(posts) }}
Определение макроса должно идти до первого вызова, иначе выйдет ошибка.
Реклама
Вместо того чтобы использовать макросы прямо в шаблоне, лучше хранить их в отдельном файле и импортировать по надобности.
Предположим, все макросы хранятся в файле macros.html в папке templates. Чтобы импортировать их из файла, нужно использовать инструкцию import:
{% import "macros.html" as macros %}
Теперь можно ссылаться на макросы в файле macros.html с помощью переменной macros. Например:
{{ macros.render_posts(posts) }}
Инструкция {% import “macros.html” as macros %} импортирует все макросы и переменные (определенные на высшем уровне) из файла macros.html в шаблон. Также можно импортировать определенные макросы с помощью from:
{% from "macros.html" import render_posts %}
При использовании макросов будут ситуации, когда потребуется передать им произвольное число аргументов.
По аналогии с *args и **kwargs в Python внутри макросов можно получить доступ к varargs и kwargs.
varags: сохраняет дополнительные позиционные аргументы, переданные макросу, в виде кортежа.
lwargs: сохраняет дополнительные позиционные аргументы, переданные макросу, в виде словаря.
Хотя к ним можно получить доступ внутри макроса, объявлять их отдельно в заголовке макроса не нужно. Вот пример:
{% macro custom_renderer(para) %}
{{ para }}
varargs: {{ varargs }}
kwargs: {{ kwargs }}
{% endmacro %}
{{ custom_renderer("some content", "apple", name='spike', age=15) }}
В этом случае дополнительный позиционный аргумент, "apple", присваивается varargs, а дополнительные аргументы-ключевые слова (name=’spike’, age=15) — kwargs.
Экранирование
Jinja по умолчанию автоматически экранирует вывод переменной в целях безопасности. Поэтому если переменная содержит, например, такой HTML-код: "
Escaping in Jinja
", он отрендерится в виде "<p>Escaping in Jinja</p>". Благодаря этому HTML-коды будут отображаться в браузере, а не интерпретироваться. Если есть уверенность, что данные безопасны и их точно можно рендерить, стоит воспользоваться фильтром safe. Например:
{% set html = "
Escaping in Jinja
" %}
{{ html|safe }}
Вывод:
Escaping in Jinja
Использовать фильтр safe в большом блоке кода будет неудобно, поэтому в Jinja есть оператор autoescape, который используется, чтобы отключить экранирование для большого объема данных. Он может принимать аргументы true или false для включения и отключения экранирования, соответственно. Например:
{% autoescape true %}
Escaping enabled
{% endautoescape %}
{% autoescape false %}
Escaping disabled
{% endautoescape %}
Все между {% autoescape false %} и {% endautoescape %} отрендерится без экранирования символов. Если нужно экранировать отдельные символы при выключенном экранировании, стоит использовать фильтр escape. Например:
{% autoescape false %}
{% for post in post_list %}
{{ post.title }}
{{ post.html }}
{% endfor %}
{% for comment in comment_list %}
{{ comment|escape }}
# escaping is on for comments
{% endfor %}
{% endautoescape %}
Вложенные шаблоны
Инструкция include рендерит шаблон внутри другого шаблона. Она широко используется, чтобы рендерить статический раздел, который повторяется в разных местах сайта. Вот синтаксис include:
Предположим, что навигационное меню хранится в файле nav.html, сохраненном в папке templates:
Чтобы добавить это меню в home.html, нужно использовать следующий код:
Title
{# добавляем панель навигации из nav.html #}
{% include 'nav.html' %}
Вывод:
Title
Наследование шаблонов
Наследование шаблонов — один из самых мощных элементов шаблонизатора Jinja. Его принцип похож на ООП (объектно-ориентированное программирование). Все начинается с создания базового шаблона, который содержит в себе скелет HTML и отдельные маркеры, которые дочерние шаблоны смогут переопределять. Маркеры создаются с помощью инструкции block. Дочерние шаблоны используют инструкцию extends для наследования или расширения основного шаблона. Вот пример:
{# Это шаблон templates/base.html #}
{% block title %}Default Title{% endblock %}
{% block nav %}
{% endblock %}
{% block content %}
{% endblock %}
Это базовый шаблон base.html. Он создает три блока с помощью block, которые впоследствии будут заполнены дочерними шаблонами. Инструкция block принимает один аргумент — название блока. Внутри шаблона это название должно быть уникальным, иначе возникнет ошибка.
Дочерний шаблон — это шаблон, который растягивает базовый шаблон. Он может добавлять, перезаписывать или оставлять элементы родительского блока. Вот как можно создать дочерний шаблон.
{# Это шаблон templates/child.html #}
{% extends 'base.html' %}
{% block content %}
{% for bookmark in bookmarks %}
{{ bookmark.title }}
{% endfor %}
{% endblock %}
Инструкция extends сообщает Jinja, что child.html — это дочерний элемент, наследник base.html. Когда Jinja обнаруживает инструкцию extends, он загружает базовый шаблон, то есть base.html, а затем заменяет блоки контента внутри родительского шаблона блоками с теми же именами из дочерних шаблонов. Если блок с соответствующим названием не найден, используется блок родительского шаблона.
Стоит отметить, что в дочернем шаблоне перезаписывается только блок content, так что содержимое по умолчанию из title и nav будет использоваться при рендеринге дочернего шаблона. Вывод должен выглядеть следующим образом:
Реклама
Default Title