Модуль Jinja2
- это современный и удобный язык шаблонов для Python, созданный по образцу шаблонов Django. Он быстр, т.к. компилируется в код Python, широко используется и безопасен благодаря дополнительной среде выполнения изолированных шаблонов:
Преимущества языка шаблонов Jinja2:
- Автоматическая система экранирования HTML для предотвращения XSS.
- Наследование шаблонов, поддержка макросов.
- Шаблоны компилируются до оптимального кода Python (можно отключить при отладке).
- При отладке, номера строк исключений точно указывают на неправильную строку в шаблоне.
- Настраиваемый синтаксис, много встроенный фильтров.
- Поддержка использования методов стандартных типов Python в шаблонах.
- Возможность вызова функций Python в шаблонах.
Установка модуля Jinja2 в виртуальное окружение:
# создаем виртуальное окружение, если нет
$ python3 -m venv .venv --prompt VirtualEnv
# активируем виртуальное окружение
$ source .venv/bin/activate
# ставим модуль Jinja2
(VirtualEnv):~$ python -m pip install -U Jinja2
Инициализация движка шаблонов Jinja2 в Python.
Модуль
Jinja
использует центральный объект, называемый шаблоном
jinja2.Environment()
. Экземпляры этого класса используются для хранения конфигурации и глобальных объектов, а также для
загрузки шаблонов из файловой системы или других мест. Даже если создавать шаблоны из строк с помощью конструктора класса
jinja2.Template()
, среда
Environment
создается автоматически, только она будет совместно используемая.
Большинство приложений создают один объект Environment
при инициализации приложения и используют его для загрузки шаблонов. Однако в некоторых случаях полезно иметь несколько сред рядом, если используются разные конфигурации.
Самый простой способ настроить Jinja для загрузки шаблонов для приложения выглядит примерно так:
from jinja2 import Environment, PackageLoader, select_autoescape
env = Environment(
loader=PackageLoader('yourapplication', 'templates'),
autoescape=select_autoescape(['html', 'xml'])
)
Это создаст шаблонную среду с настройками по умолчанию и загрузчиком
loader
, который будет искать шаблоны в папке шаблонов
templates
внутри пакета python
yourapplication
.
Доступны разные загрузчики, также можно написать свой собственный, если необходимо загружать шаблоны из базы данных или других ресурсов. Код примера, так же, определяет автоматическое экранирование файлов с расширениями
.html
и
.xml
.
Чтобы загрузить шаблон из среды
env
, которая определены в примере выше, нужно просто вызвать метод
env.get_template()
, который затем возвращает загруженный шаблон
Template
:
template = env.get_template('mytemplate.html')
Чтобы отобразить его с некоторыми переменными, просто вызовите метод Template.render()
:
print(template.render(the='variables', go='here'))
Использование
загрузчика шаблонов вместо передачи обычных
строк в
Template
или
Environment.from_string()
имеет несколько преимуществ. Помимо того, что загрузчик намного проще в использовании, он также позволяет
наследование шаблонов.
Примечания по автоэкранированию.
В будущих версиях Jinja, автоматическое экранирование будет включено по умолчанию из соображений безопасности. Рекомендуется явно
настроить автоматическое экранирование, а не полагаться на значение по умолчанию.
Примечания по идентификаторам в шаблонах.
Jinja использует правила именования Python. Допустимые идентификаторы могут быть любой комбинацией символов Юникода, принятой Python.
Фильтры ищутся в отдельных пространствах имен и имеют слегка измененный синтаксис идентификатора. Фильтры могут содержать точки для группировки по темам. Например, вполне допустимо добавить метод в фильтр и вызвать его как
.unicode
. Регулярное выражение для проверки идентификаторов фильтров:
[a-zA-Z_][a-zA-Z0-9_]*(\.[a-zA-Z_][a-zA-Z0-9_]*)*
.
Базовое использование движка шаблонов Jinja2.
В этом разделе дается краткое введение в Python API для шаблонов Jinja.
Самый простой способ создать шаблон и отрендерить его - использовать класс
jinja2.Template()
. Такой способ работы не рекомендуется, если шаблоны загружаются не из строк, а из файловой системы или другого источника данных:
>>> from jinja2 import Template
>>> template = Template('Hello {{ name }}!')
>>> template.render(name='John Doe')
# 'Hello John Doe!'
>>> content = {'a': 5, 'b': 2}
>>> tpl = 'Сумма чисел {{ a }} и {{ b }} равна {{ a + b }}'
>>> Template(tpl).render(content)
# 'Сумма чисел 5 и 2 равна 7'
Пример разбора шаблона с циклом.
>>> import jinja2
# шаблон с циклом
>>> tpl = """{{ title }}
... {{ '-' * title|length }}
... {% for n, user in enumerate(users, 1) %}
... {{ n }}. {{ user.name }} - должность: {{ user.status }}, оклад: ${{ user.salary }}
... {% endfor %}
... """
# собираем данные для шаблона
>>> content = {}
>>> content['title'] = 'Итерация по пользователям'
>>> content['users'] = []
>>> content['users'].append({'name': 'Маша', 'status': 'Менеджер', 'salary': 1500})
>>> content['users'].append({'name': 'Света', 'status': 'Дизайнер', 'salary': 1000})
>>> content['users'].append({'name': 'Игорь', 'status': 'Программист', 'salary': 2000})
# В словаре передаем в шаблон функцию Python
>>> content['enumerate'] = enumerate
# Смотрим, что получилось
>>> print(jinja2.Template(tpl, trim_blocks=True).render(content))
# Итерация по пользователям
# -------------------------
# 1. Маша - должность: Менеджер, оклад: $1500
# 2. Света - должность: Дизайнер, оклад: $1000
# 3. Игорь - должность: Программист, оклад: $2000
Загрузка шаблонов из файловой системы.
Сохраним шаблон из предыдущего примера в директорию ~/temp/main.txt
.
{# файл ~/temp/main.txt #}
{{ title }}
{# Добавим условие #}
{% if title %}
{# Если существует переменная `title`, то будем ее подчеркивать #}
{{ '-' * title|length }}
{% endif %}
{# Цикл по пользователям #}
{% for n, user in enumerate(users, 1) %}
{{ n }}. {{ user.name }} - должность: {{ user.status }}, оклад: ${{ user.salary }}
{% endfor %}
Основной код программы, которая работает с сохраненным шаблоном main.txt
. Для понимания, что происходит, код снабжен подробными комментариями.
import jinja2
# Определяем класс загрузчика шаблонов из файловой системы
# (`temp` - папка где лежит сохраненный шаблон 'main.txt')
loader = jinja2.FileSystemLoader('temp')
# Определяем переменную среду,
# в которую передаем загрузчик
env = jinja2.Environment(loader=loader, trim_blocks=True)
# данные для шаблона
content = {}
content['title'] = 'Итерация по пользователям'
content['users'] = []
content['users'].append({'name': 'Маша', 'status': 'Менеджер', 'salary': 1500})
content['users'].append({'name': 'Света', 'status': 'Дизайнер', 'salary': 1000})
content['users'].append({'name': 'Игорь', 'status': 'Программист', 'salary': 2000})
# В словаре передаем в шаблон функцию Python
content['enumerate'] = enumerate
# загружаем шаблон 'main.txt'
tpl = env.get_template('main.txt')
# рендерим шаблон в переменную `result`
result = tpl.render(content)
# Сохраним получившийся текст
with open('result.txt', 'w') as fp:
fp.write(result)
# Прочитаем записанный файл
with open('result.txt', 'r') as fp:
print(fp.read())
# Итерация по пользователям
# -------------------------
# 1. Маша - должность: Менеджер, оклад: $1500
# 2. Света - должность: Дизайнер, оклад: $1000
# 3. Игорь - должность: Программист, оклад: $2000