Фреймворк Flask использует модуль jinja2
в качестве механизма шаблонов. Можно использовать другой механизм шаблонов, но все равно нужно установить Jinja2 для запуска самого Flask. Это требование необходимо для включения многофункциональных расширений, т.к. расширение может зависеть от присутствия Jinja2 в среде выполнения приложения Flask.
В этом разделе дается очень краткое введение в то, как Jinja2 интегрируется во Flask. Если нужна информация о самом синтаксисе шаблонизатора, то за дополнительной информацией обратитесь к документации шаблонизатора Jinja2.
По умолчанию Flask, настраивает Jinja2 следующим образом:
flask.render_template()
- автоматическое экранирование включено для всех шаблонов, оканчивающихся на .html
, .htm
, .xml
, а также .xhtml
.flask.render_template_string()
автоматическое экранирование включено для всех строк.{% autoescape %}
.По умолчанию в шаблонах приложения Jinja2 доступны следующие глобальные переменные:
config
- текущий объект конфигурации flask.config
.request
- текущий объект запроса flask.request
. Переменная не будет доступна, если шаблон был визуализирован без активного контекста запроса.session
- текущий объект сессии/сеанса flask.session
. Переменная не будет доступна, если шаблон был визуализирован без активного контекста запроса.g
- глобальных объект с привязкой к запросу flask.g
. Переменная не будет доступна, если шаблон был визуализирован без активного контекста запроса.url_for()
- функция flask.url_for()
.get_flashed_messages()
- функция flask.get_flashed_messages()
.Приведенные выше объекты добавляются в контекст переменных, и они не являются глобальными переменными шаблона. Это говорит о том, что по умолчанию они не отображаются в контексте шаблонов, которые импортируются в вызываемый шаблон из функции-представления. Частично это вызвано соображениями производительности, частично для ясности.
Что это значит? Если есть макрос, который необходимо импортировать, то он должен получить доступ к объекту запроса, для этого есть две возможности:
request
макросу в качестве параметра или атрибута интересующего объекта запроса.with context
.Импорт с контекстом выглядит следующим образом:
{% from '_helpers.html' import my_macro with context %}
Автоэкранирование - это концепция автоматического экранирования специальных символов HTML. Специальные символы HTML или XML, и, следовательно, XHTML - это &
, >
, <
, "
а также '
. Так как эти символы, сами по себе, несут определенные значения, то в простом тексте HTML-документа, они должны заменяться, на так называемые "HTML-объекты". Невыполнение этого требования вызовет разочарование пользователя из-за невозможности использовать эти символы в тексте, но также может привести к проблемам с безопасностью веб-приложения на Flask.
Иногда просто необходимо отключить автоматическое экранирование в шаблонах. Это может произойти, если явно внедрить HTML в страницы, например из системы, которая генерирует безопасный HTML, такой как markdown
конвертер в HTML.
Этого можно добиться тремя способами:
flask.Markup()
. Это рекомендуемый способ.|safe
, чтобы явно пометить строку как безопасный HTML. Например {{myvariable | safe}}
.Для временного отключения системы автоэкранирования в шаблонах, можно использовать блок {% autoescape%}
:
{% autoescape false %} <p>autoescaping is disabled here <p>{{ will_not_be_escaped }} {% endautoescape %}
Когда, таким образом отключаете автоэкранирование, то будьте очень осторожны с переменными, которые используете в этом блоке!
Если необходимо зарегистрировать свои собственные фильтры для использования в шаблонах приложения Flask, то есть два способа сделать это. Можно поместить их вручную в app.jinja_env
(это по сути jinja2.Environment
) приложения или использовать декоратор @app.template_filter()
.
Два следующих примера работают одинаково:
@app.template_filter('reverse') def reverse_filter(s): return s[::-1] def reverse_filter(s): return s[::-1] app.jinja_env.filters['reverse'] = reverse_filter
В случае декоратора, аргумент является необязательным, если надо использовать имя функции в качестве имени фильтра. После регистрации можно использовать фильтр в шаблонах веб-приложения Flask так же, как встроенные фильтры Jinja2.
Например, если есть в переданном контексте список с именем mylist
:
{% for x in mylist | reverse %} {% endfor %}
Чтобы автоматически вводить новые переменные в контекст шаблона, во Flask существуют процессоры контекста. Процессоры контекста запускаются до рендеринга шаблона и имеют возможность вставлять новые значения в контекст шаблона. Обработчик контекста - это функция, возвращающая словарь. Затем ключи и значения этого словаря объединяются с контекстом шаблона для всех шаблонов в приложении:
@app.context_processor def inject_user(): return dict(user=g.user)
Вышеупомянутый обработчик контекста делает переменную с именем user
доступной в шаблоне со значением g.user
. Этот пример не очень интересен, потому что объект flask.g
в любом случае доступен в шаблонах, но он дает представление о том, как это работает.
Переменные не ограничиваются значениями. Процессор контекста также может делать доступными в шаблонах целые функции, т.к. Python позволяет передавать функции:
@app.context_processor def utility_processor(): def format_price(amount, currency="€"): return f"{amount:.2f}{currency}" return dict(format_price=format_price)
Вышеупомянутый обработчик контекста делает функцию format_price()
доступной для всех шаблонов:
{{ format_price(0.33) }}
Этот пример демонстрирует, как передавать функции в обработчик контекста, но также можно создать format_price()
как пользовательский фильтр шаблонов (см. "Регистрация пользовательских фильтров").
Самая мощная часть шаблонизатора Jinja2 - это наследование шаблонов. Наследование шаблонов позволяет создать базовый шаблон, который содержит все общие элементы сайта и определяет блоки, которые дочерние шаблоны могут переопределить.
Звучит сложно, но это очень просто.
Это базовый шаблон layout.html
, который определяет скелет HTML-документа, который можно использовать для страницы с двумя столбцами. Задача "дочерних" шаблонов - заполнить пустые блоки контентом:
<!doctype html> <html> <head> {% block head %} <link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}"> <title>{% block title %}{% endblock %} - My Webpage</title> {% endblock %} </head> <body> <div id="content">{% block content %}{% endblock %}</div> <div id="footer"> {% block footer %} © Copyright 2021 by <a href="http://domain.name/">you</a>. {% endblock %} </div> </body> </html>
В этом примере теги {% block %}
определяют четыре блока, которые могут заполнять дочерние шаблоны. Все, что делает тег block
- это сообщает механизму шаблонов, что дочерний шаблон может переопределить эти части шаблона.
Дочерний шаблон может выглядеть так:
{% extends "layout.html" %} {% block title %}Index{% endblock %} {% block head %} {{ super() }} <style type="text/css"> .important { color: #336699; } </style> {% endblock %} {% block content %} <h1>Index</h1> <p class="important">Welcome on my awesome homepage.</p> {% endblock %}
Здесь тег {% extends%}
является ключевым. Он сообщает механизму шаблонов, что этот шаблон "расширяет" другой шаблон. Когда система шаблонов оценивает этот шаблон, то сначала определяет местонахождение родителя layout.html
. Тег extends
должен быть первым тегом в шаблоне. Чтобы отобразить содержимое блока, который определен в родительском шаблоне, нужно использовать {{ super() }}
.