Рендеринг шаблонов Flask
Конкатенация HTML-строк в коде Python — это и утомительно, и опасно (подвержено XSS-уязвимостям). Flask интегрирует шаблонизатор Jinja2, чтобы решить эту проблему.
Шаблоны позволяют отделить бизнес-логику от представления страницы, писать более безопасный и удобный в поддержке код.
Основы render_template
render_template()Это наиболее часто используемая функция рендеринга шаблонов во Flask.
Её первый параметр — имя файла шаблона, а следующие именованные аргументы передаются в шаблон как переменные.
Пример
from flask import Flask, render_template
app = Flask(__name__)
@app.route("/")
def index():
# Отрендерить шаблон templates/index.html, передав переменные title и name
return render_template("index.html", title=EXAMPLE Home, name="World")
@app.route("/user/<username>")
def profile(username):
# Передать username из URL в шаблон
return render_template("user.html", username=username, posts=[
{"title": Введение во Flask, "date": "2026-05-01"},
{"title": Шаблон Jinja2, "date": "2026-05-10"},
])
Файлы шаблонов размещаются в каталоге проектаtemplates/В папке:
example-flask-test/
├── app.py
└── templates/
├── index.html
└── user.html
Содержимое index.html:
Пример (index.html)
<!DOCTYPE html>
<html>
<head>
<title>{{ title }} - ПРИМЕР</title>
</head>
<body>
<h1>{{ title }} </h1>
<p>Привет {{ name }}。</p>
</body>
</html>
Посетитеhttp://127.0.0.1:5000/, результат вывода:

Базовый синтаксис Jinja2
Три основных синтаксиса в шаблонах Jinja2:
| Синтаксис | Назначение | Пример |
|---|---|---|
| {{ ... }} | Вывод значений переменных, автоматическое экранирование HTML | {{ username }} |
| {% ... %} | Управляющие операторы (if, for, block и т. д.) | {% if user %} |
| {# ... #} | Комментарии, не появятся в отрендеренном результате | {# Это комментарий #} |
Пример шаблона
Пример (user.html)
<!DOCTYPE html>
<html>
<head>
<title>{{ username }} - ПРИМЕР</title>
</head>
<body>
<h1>Личная домашняя страница {{ username }}</h1>
<!-- Условие: если список posts пуст, вывести подсказку -->
{% if posts %}
<h2>Последние статьи:</h2>
<ul>
<!-- Цикл по списку posts -->
{% for post in posts %}
<!-- loop.index — встроенная переменная Jinja2, начинается с 1 -->
<li>{{ loop.index }}. {{ post.title }} ({{ post.date }})</li>
{% endfor %}
</ul>
{% else %}
<p>Пока нет статей</p>
{% endif %}
<!-- Использование фильтра: upper преобразует текст в верхний регистр -->
<p>Имя пользователя в верхнем регистре: {{ username|upper }}</p>
</body>
</html>
Общие фильтры
Фильтры используются для изменения формата вывода переменных с помощью символа вертикальной черты.|Вызов.
| Filter | Функция | Пример | Вывод |
|---|---|---|---|
| upper | Преобразовать в верхний регистр | {{ "hello"|upper }} | HELLO |
| lower | Преобразовать в нижний регистр | {{ "HELLO"|lower }} | привет |
| title | Сделать первую букву заглавной | {{ "hello world"|title }} | Hello World |
| length | Получить длину | {{ [1,2,3]|length }} | 3 |
| default | Установить значение по умолчанию | {{ name|default("anonymous") }} | Аноним (когда name пусто) |
| safe | Пометить как безопасный HTML (без экранирования) | {{ "Жирный"|safe }} | Отображается как жирный текст |
| join | Объединить список | {{ ["a","b"]|join(",") }} | a,b |
Предупреждение о безопасности:safeФильтр safe отключает автоэкранирование; используйте его только тогда, когда вы абсолютно доверяете источнику данных. Никогда не используйте его для пользовательского ввода.safe。
Автоэкранирование — защита от XSS
Jinja2 по умолчанию экранирует HTML во всех выводимых переменных; это ключевая защита для веб-безопасности.
Например, если имя пользователя, отправленное пользователем, —<script>alert("xss")</script>, то при отрисовке шаблона оно автоматически преобразуется в безопасный текст:
<!-- 模板中直接使用变量 -->
<p>{{ username }}</p>
<!-- 渲染结果(HTML 特殊字符已被转义) -->
<p>&lt;script&gt;alert(&quot;xss&quot;)&lt;/script&gt;</p>
Экранирование применяется к.html、.htm、.xml、.xhtml、.svgФайл шаблона в конце.
Наследование шаблонов — устранение дублирования кода
Наследование шаблонов — одна из самых мощных возможностей Jinja2. Оно позволяет определить "базовый макет", а затем заполнять содержимое через дочерние шаблоны.
Это позволяет избежать повторного написания общих частей, таких как заголовки, навигация, нижние колонтитулы и т. д., на каждой странице.
Базовый шаблон (родительский шаблон)
Пример
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>{% block title %}ПРИМЕР: Руководство по Flask{% endblock %}</title>
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
</head>
<body>
<header>
<h1>ПРИМЕР: Руководство по Flask</h1>
<nav>
<a href="/">Главная</a> |
<a href="/about">О нас</a>
</nav>
</header>
<main>
<!-- block — это область-заполнитель, которую дочерние шаблоны могут заполнять или переопределять -->
{% block content %}{% endblock %}
</main>
<footer>
<p>© 2026 example.com</p>
</footer>
</body>
</html>
Дочерний шаблон (наследует родительский шаблон)
Пример
{% extends "base.html" %}
<!-- Переопределяет блок title родительского шаблона -->
{% block title %}EXAMPLE Tutorial - Главная{% endblock %}
<!-- Заполняет блок content родительского шаблона -->
{% block content %}
<h2>Добро пожаловать в EXAMPLE</h2>
<p>{{ greeting }}</p>
{% if user %}
<p>Текущий пользователь: {{ user }}</p>
{% else %}
<p><a href="/login">Пожалуйста, сначала войдите в систему</a></p>
{% endif %}
{% endblock %}
| Теги | Функция |
|---|---|
| {% extends "base.html" %} | Объявите, что текущий шаблон наследуется от base.html (должно быть в первой строке) |
| {% block name %}...{% endblock %} | Определите блок, который может быть переопределён дочерними шаблонами |
| {{ super() }} | Вызвать содержимое одноимённого блока родительского шаблона внутри блока дочернего шаблона. |
После использования наследования шаблонов вам нужно изменить толькоbase.htmlв одном месте, и общие части всех страниц (такие как навигация, нижний колонтитул) будут автоматически обновляться.
Встроенные объекты в шаблонах
Следующие объекты Flask можно использовать непосредственно в шаблонах, не передавая их черезrender_template():
| Object | Описание | Пример использования в шаблоне |
|---|---|---|
| request | Текущий объект запроса | {{ request.path }} |
| session | Текущие данные сессии | {{ session.get("username") }} |
| g | Глобальные переменные уровня запроса | {{ g.user }} |
| config | Конфигурация приложения | {{ config["APP_NAME"] }} |
| url_for() | Функция генерации URL | {{ url_for("index") }} |
| get_flashed_messages() | Получить flash-сообщения | {% for msg in get_flashed_messages() %}... |
Включение других шаблонов — include
Использование{% include %}Вы можете встроить другой шаблон внутри одного шаблона:
Пример
<nav style="background:#f0f0f0;padding:10px;">
<a href="/">Главная</a> |
<a href="/posts">Статьи</a> |
<a href="/about">О нас</a>
</nav>
Пример
<!DOCTYPE html>
<html>
<head><title>Страница</title></head>
<body>
{% include "_navbar.html" %}
<h1>Содержимое страницы</h1>
<p>Это основное содержимое страницы.</p>
</body>
</html>
Лучшие практики: Наследование шаблонов (extends) используется для повторного использования макета уровня страницы,includeИспользуется для повторного использования кода на уровне компонентов. Повторно используемые компоненты, такие как панели навигации и боковые панели, подходят для include.
Полный пример: Главная страница блога
Примените вышеописанные знания в реальном сценарии:
Пример
from flask import Flask, render_template
app = Flask(__name__)
@app.route("/")
def index():
# Имитация данных статей в базе данных.
articles = [
{"id": 1, "title": Руководство по началу работы с Flask, "author": "example", "views": 1024},
{"id": 2, "title": Шаблоны Jinja2: объяснение, "author": "admin", "views": 512},
{"id": 3, "title": Проектирование RESTful API, "author": "example", "views": 256},
]
return render_template("blog.html", articles=articles)
Пример
{% extends "base.html" %}
{% block title %}ПРИМЕР Блог{% endblock %}
{% block content %}
<h2>Последние статьи</h2>
{% if articles %}
{% for article in articles %}
<!-- Добавить разные стили в зависимости от количества просмотров -->
<div class="article" {% if article.views > 1000 %}style="background:#fff3cd;"{% endif %}>
<h3>{{ article.title }}</h3>
<p>Автор: {{ article.author }} | Просмотры: {{ article.views }}</p>
<!-- Используйте переменную цикла для создания разделителя -->
{% if not loop.last %}
<hr>
{% endif %}
</div>
{% endfor %}
{% else %}
<p>Пока нет статей, пожалуйста, заходите позже.</p>
{% endif %}
{% endblock %}
Специальные переменные Jinja2
| Переменная | Описание |
|---|---|
| loop.index | Текущий индекс цикла, начинается с 1 |
| loop.index0 | Текущий индекс цикла, начинается с 0 |
| loop.first | Является ли текущая итерация первой в цикле |
| loop.last | Является ли текущая итерация последней в цикле |
| loop.length | Общая длина последовательности |