Подключить шаблоны Jinja2 во Flask: инструкция для новичка
Flask уже поставляется с Jinja2 «из коробки», но чтобы шаблоны заработали, нужно правильно разложить файлы и разобраться с синтаксисом. Пошаговая настройка от пустого проекта до рабочей страницы — за 20 минут.

Что понадобится до начала
Прежде чем создавать первый шаблон, убедитесь, что у вас есть:
- Python 3.9 или новее (проверьте командой
python3 --version); - pip — менеджер пакетов Python (обычно идёт в комплекте);
- текстовый редактор или IDE — VS Code, PyCharm, даже nano подойдёт;
- терминал — встроенный в систему или в редакторе.
Jinja2 отдельно ставить не придётся: Flask тянет его как зависимость. Вся инструкция написана для Flask 3.x.
Шаг 1. Создайте проект и установите Flask
Создайте каталог, активируйте виртуальное окружение и поставьте Flask:
mkdir flask_jinja_demo
cd flask_jinja_demo
python3 -m venv venv
source venv/bin/activate # Windows: venv\Scripts\activate
pip install flask
Проверка: выполните pip show flask. В выводе должна быть строка Name: Flask и версия 3.x. Там же в разделе Requires вы увидите Jinja2 — он уже установлен.
Шаг 2. Разложите файлы правильно
Flask ищет шаблоны в каталоге templates рядом с файлом приложения. Если каталога нет или он назван иначе — получите ошибку TemplateNotFound. Вот минимальная структура:
flask_jinja_demo/
├── app.py
├── templates/
│ └── index.html
└── venv/
Имя templates — значение по умолчанию. Его можно изменить параметром template_folder при создании приложения, но на старте лучше следовать соглашению.
Проверка: в корне проекта выполните ls templates (или dir templates на Windows). Вы должны увидеть index.html.
Шаг 3. Напишите минимальное приложение
Создайте файл app.py со следующим содержимым:
# app.py — минимальное Flask-приложение с шаблоном
from flask import Flask, render_template
app = Flask(__name__)
@app.route("/")
def index():
# Передаём переменную page_title в шаблон
return render_template("index.html", page_title="Главная страница")
if __name__ == "__main__":
app.run(debug=True)
Ключевая функция — render_template. Первый аргумент — имя файла внутри templates/, остальные — именованные переменные, которые станут доступны в шаблоне.
Шаг 4. Создайте шаблон
Откройте templates/index.html и вставьте:
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<title>{{ page_title }}</title>
</head>
<body>
<h1>{{ page_title }}</h1>
<p>Шаблон Jinja2 работает.</p>
</body>
</html>
Двойные фигурные скобки {{ }} — это точки вывода. Jinja2 подставит значение переменной page_title, которое вы передали из Python.
Проверка: запустите приложение командой python3 app.py и откройте в браузере http://127.0.0.1:5000. Вы должны увидеть заголовок «Главная страница» и текст под ним.
Шаг 5. Добавьте логику в шаблон
Jinja2 поддерживает условия, циклы и фильтры прямо в HTML. Расширим пример — передадим список элементов и выведем их:
Обновите маршрут
@app.route("/")
def index():
tasks = [
{"name": "Изучить Jinja2", "done": True},
{"name": "Написать шаблон", "done": False},
{"name": "Запустить сервер", "done": True},
]
return render_template("index.html", page_title="Мои задачи", tasks=tasks)
Обновите шаблон
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<title>{{ page_title }}</title>
</head>
<body>
<h1>{{ page_title }}</h1>
{% if tasks %}
<ul>
{% for task in tasks %}
<li>
{{ task.name }} —
{% if task.done %}
готово
{% else %}
в работе
{% endif %}
</li>
{% endfor %}
</ul>
{% else %}
<p>Задач пока нет.</p>
{% endif %}
</body>
</html>
Конструкции {% %} — это управляющие теги: циклы, условия, блоки наследования. Они не выводятся в итоговый HTML.
Проверка: перезагрузите страницу в браузере (Flask в режиме debug=True перезапускается автоматически). Вы увидите маркированный список из трёх задач с пометками «готово» или «в работе».
Шаг 6. Подключите наследование шаблонов
Копировать <head> и навигацию в каждый файл — путь к рассинхрону. Jinja2 решает это через наследование: вы создаёте базовый шаблон, а дочерние переопределяют только нужные блоки.
Базовый шаблон templates/base.html
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="UTF-8">
<title>{% block title %}Без названия{% endblock %}</title>
</head>
<body>
<nav>Навигация сайта</nav>
<main>
{% block content %}{% endblock %}
</main>
<footer>© 2025</footer>
</body>
</html>
Дочерний шаблон templates/index.html
{% extends "base.html" %}
{% block title %}{{ page_title }}{% endblock %}
{% block content %}
<h1>{{ page_title }}</h1>
{% if tasks %}
<ul>
{% for task in tasks %}
<li>{{ task.name }} — {{ "готово" if task.done else "в работе" }}</li>
{% endfor %}
</ul>
{% else %}
<p>Задач пока нет.</p>
{% endif %}
{% endblock %}
Директива {% extends "base.html" %} должна стоять первой строкой в файле. Всё, что вне блоков, игнорируется.
Проверка: откройте страницу, просмотрите исходный код в браузере (Ctrl+U). Вы увидите навигацию и футер из базового шаблона, а между ними — содержимое из index.html.
Наследование шаблонов — главная причина, по которой Jinja2 экономит время. Без него каждое изменение шапки сайта превращается в правку десятков файлов.
Краткая шпаргалка по синтаксису Jinja2
| Синтаксис | Назначение | Пример |
|---|---|---|
{{ }} | Вывод переменной | {{ user.name }} |
{% %} | Управляющая конструкция | {% for item in list %} |
{# #} | Комментарий (не попадает в HTML) | {# TODO: добавить пагинацию #} |
| | Фильтр | {{ name|upper }} |
{% block %} | Определение блока для наследования | {% block content %}{% endblock %} |
{% extends %} | Наследование от родительского шаблона | {% extends "base.html" %} |
Сноска: фильтр в Jinja2 — это функция, которая трансформирует значение при выводе. Пишется через символ | после переменной: {{ text|truncate(100) }} обрежет строку до 100 символов. Flask добавляет несколько собственных фильтров к стандартному набору Jinja2.
Если не получилось
- Ошибка
jinja2.exceptions.TemplateNotFound: index.html— каталогtemplatesне найден или назван иначе. Проверьте, что он лежит рядом сapp.pyи что в имени нет опечатки (например,templateбезs). - Переменная выводится как пустая строка — имя в
render_templateне совпадает с именем в шаблоне. Python чувствителен к регистру:pageTitleиpage_title— разные переменные. SyntaxErrorв шаблоне при{% extends %}— директива не стоит первой строкой. Перед ней не должно быть ни пробелов с HTML, ни других тегов Jinja2.- Страница не обновляется после правки шаблона — убедитесь, что Flask запущен с
debug=True. В продакшн-режиме шаблоны кешируются, и для обновления нужен перезапуск. UndefinedError: 'tasks' is undefined— вы обращаетесь к переменной, которую не передали. Добавьтеtasks=tasksв вызовrender_templateили оберните вывод в{% if tasks is defined %}.
Что делать дальше
Шаблоны работают — пора двигаться дальше:
- Подключите статику (CSS, JS, изображения) через функцию
url_for('static', filename='style.css')и каталогstatic/. - Разберитесь с макросами Jinja2 — это аналог функций внутри шаблонов, удобный для повторяющихся HTML-блоков вроде карточек или форм.
- Изучите автоэкранирование: Flask включает его по умолчанию для файлов
.html, что защищает от XSS. Если вам нужно вывести «сырой» HTML, используйте фильтр|safe, но только для данных, которым вы доверяете.
Официальная документация Jinja2 и Flask — лучший следующий шаг. Темы шаблонов, которые стоит изучить отдельно: включение через {% include %}, расширения и кастомные фильтры.
Антиблок-аналитик 115-ФЗ: Анализ выписок, поиск рисков блокировки счёта, инструкции по безопасным транзакциям.
Спросить за 41 ₽Источники: Flask — Quickstart: Rendering Templates (официальная документация), Jinja2 — Template Designer Documentation
Частые вопросы
Нужно ли ставить Jinja2 отдельно через pip?
Можно ли хранить шаблоны не в папке templates?
Что если у меня другая версия Flask — 2.x или старше?
Безопасно ли выводить пользовательские данные через {{ }}?
Как передать в шаблон данные из базы данных?
Шаблон не обновляется после правки — что делать?
Материал носит информационный характер и подготовлен редакцией «Агентуры». Он не является офертой, рекламой или индивидуальной консультацией. Упомянутые продукты, компании и торговые знаки принадлежат их правообладателям. Перед принятием решений, влекущих юридические или финансовые последствия, обратитесь к профильному специалисту.