Введение в Jinja2 и render_template
Шаблон — это HTML с дырками для данных. Jinja2 берёт шаблон, подставляет переданные значения и возвращает готовую страницу. View считает данные, шаблон их показывает.
Собирать HTML строками в Python — ад: кавычки, экранирование, перемешанная логика. Jinja2 разделяет «что показать» (данные из view) и «как показать» (разметка в шаблоне). Это и есть архитектурный принцип разделения ответственности.
Представь страницу профиля: имя, аватар, список постов. Если клеить её строками в Python — получится нечитаемое месиво. Шаблонизатор переворачивает подход: ты пишешь почти обычный HTML, а в нужных местах ставишь {{ переменная }}. Flask использует Jinja2 — мощный и популярный движок.
Шаблоны кладут в папку templates рядом с приложением. View вызывает render_template с именем файла и данными:
from flask import render_template
@app.route("/hello/<name>")
def hello(name):
return render_template("hello.html", name=name, items=["a", "b"])
А сам шаблон templates/hello.html:
<h1>Привет, {{ name }}!</h1>
<ul>
{% for item in items %}
<li>{{ item }}</li>
{% endfor %}
</ul>
Две конструкции: {{ ... }} выводит значение, {% ... %} — управляющая логика (циклы, условия). Это и есть язык Jinja.
Разделение данных и представления — это не вкусовщина, а принцип, который делает код поддерживаемым. Когда HTML собирается строками в Python, любая правка вёрстки означает копание в логике, а дизайнер вообще не может работать с таким кодом. Шаблоны переворачивают расклад: разметка живёт в .html-файлах, понятных даже верстальщику, а Python лишь поставляет данные. Это и есть архитектурный паттерн MVC в миниатюре: модель (данные), представление (шаблон), контроллер (view). Держи шаблоны «тупыми» — вся подготовка данных делается во view, а шаблон только показывает готовое. Тогда и логику, и вёрстку можно менять независимо, не ломая друг друга.
Как работает под капотом
Jinja не подставляет строки наивно — он компилирует шаблон в Python-функцию один раз, а потом быстро вызывает её с данными. Грубо говоря, {{ name }} превращается в кусок кода, который пишет значение name в буфер вывода.
hello.html (текст)
│ компиляция (один раз)
▼
Python-функция render(context)
│ вызов с {name:"Анна", items:[...]}
▼
готовый HTML-текст
│
▼
Response браузеру
Чтобы прочувствовать суть подстановки, смоделируем мини-шаблонизатор: заменим {{ ключ }} на значения из словаря.
import re
def render(template, **context):
def repl(m):
key = m.group(1).strip()
return str(context.get(key, ""))
return re.sub(r"\{\{\s*(\w+)\s*\}\}", repl, template)
tpl = "Привет, {{ name }}! Тебе {{ age }} лет."
print(render(tpl, name="Анна", age=15))
print(render(tpl, name="Боб", age=20))
Запусти — один шаблон, разные данные, разный результат. Настоящий Jinja умеет циклы, условия, наследование и автоэкранирование, но базовая идея ровно такая.
Частые ошибки
- Шаблон не в папке templates. Flask ищет шаблоны именно там; иначе TemplateNotFound.
- Логику бизнеса тащить в шаблон. Шаблон — про отображение. Вычисления делай во view.
- Путать {{ }} и {% %}. Двойные фигурные выводят значение, процентные — управляют потоком.
Best practices
- Передавай в шаблон уже готовые данные, не сырые объекты с тяжёлой логикой.
- Имена шаблонов осмысленные, структура папок повторяет блюпринты.
- Держи шаблоны «тупыми»: минимум логики, максимум разметки.
Что запомнить
- Шаблон отделяет представление (HTML) от логики (данные из view).
- render_template ищет файлы в папке templates и подставляет данные.
- {{ }} выводит значение, {% %} — управляющая логика.
- Jinja компилирует шаблон в Python-функцию для скорости.
Итог: Jinja2 разделяет данные и представление. View готовит данные, render_template отдаёт их шаблону, шаблон через {{ }} и {% %} собирает HTML. Дальше — синтаксис Jinja подробно.