Синтаксис Jinja: переменные, циклы, условия, фильтры
Jinja — это маленький язык внутри HTML: переменные, циклы, условия и фильтры. Их хватает, чтобы собрать любую страницу из данных.
Логика шаблона ограничена намеренно: можно перебрать список, проверить условие, применить фильтр — но не написать сложный алгоритм. Это дисциплина: тяжёлые вычисления остаются в Python, шаблон только отображает.
Разберём ядро синтаксиса. Переменные выводятся через {{ }}, причём можно обращаться к атрибутам и элементам: {{ user.name }}, {{ items[0] }}, {{ data['key'] }}. Циклы и условия — через {% %}.
{% if user %}
<p>Привет, {{ user.name }}!</p>
{% else %}
<p>Войдите в систему</p>
{% endif %}
<ul>
{% for post in posts %}
<li>{{ loop.index }}. {{ post.title }}</li>
{% else %}
<li>Постов пока нет</li>
{% endfor %}
</ul>
Внутри for доступна переменная loop: loop.index (номер с 1), loop.first, loop.last. Блок {% else %} в цикле срабатывает, если список пуст — удобно для «нет данных».
Фильтры — функции через вертикальную черту: {{ name|upper }}, {{ price|round(2) }}, {{ text|truncate(100) }}, {{ items|length }}, {{ value|default('—') }}. Фильтры можно цепочкой: {{ name|trim|capitalize }}.
Ограниченность логики в шаблонах — это фича, а не недостаток. Jinja намеренно не даёт писать сложные алгоритмы, чтобы тяжёлые вычисления оставались в Python, где их легко тестировать и переиспользовать. Если внутри {% %} начинает зарождаться алгоритм с вложенными условиями — это сигнал вынести его во view или в кастомный фильтр. Фильтры, кстати, — самый элегантный способ повторно применять преобразования: дату форматируешь одним фильтром во всех шаблонах, и логика форматирования живёт в одном месте. Переменная loop с её index, first, last и length закрывает почти все потребности в нумерации и в особой отрисовке первого/последнего элемента без ручных счётчиков и подсчётов длины.
Как работает под капотом
Фильтр — это просто функция, которой значение слева передаётся первым аргументом. name|upper эквивалентно upper(name), а price|round(2) — round(price, 2). Цепочка фильтров — это вложенные вызовы слева направо.
{{ name | trim | capitalize }}
│ │ │
▼ ▼ ▼
" bob " "bob" "Bob"
значение trim() capitalize()
(вход) фильтр1 фильтр2
Смоделируем конвейер фильтров обычным Python — увидишь, что это просто последовательное применение функций.
FILTERS = {
"upper": str.upper,
"capitalize": str.capitalize,
"trim": str.strip,
"length": len,
}
def apply_filters(value, *filter_names):
for name in filter_names:
value = FILTERS[name](value)
return value
print(apply_filters(" bob ", "trim", "capitalize")) # "Bob"
print(apply_filters("flask", "upper")) # "FLASK"
print(apply_filters([1, 2, 3], "length")) # 3
Запусти: значение проходит сквозь цепочку функций слева направо. В Jinja десятки встроенных фильтров и можно регистрировать свои через декоратор template_filter.
Частые ошибки
- Забыть endfor/endif. Jinja требует явного закрытия блоков, иначе ошибка синтаксиса.
- Сложная логика в шаблоне. Если внутри {% %} начинается алгоритм — вынеси его во view.
- Путать фильтр и функцию. Фильтр — через |, значение идёт первым аргументом.
Best practices
- Используй loop.index, loop.first/last вместо ручных счётчиков.
- Применяй фильтр default для отсутствующих значений вместо if-обёрток.
- Свои частые преобразования оформляй кастомными фильтрами, а не копипастой.
Что запомнить
- Ядро Jinja: переменные, циклы for, условия if, фильтры через |.
- Фильтр — функция, куда значение слева идёт первым аргументом; их можно цепочкой.
- Переменная loop даёт index, first, last; {% else %} в for — для пустого списка.
- Тяжёлые вычисления остаются в Python, шаблон только отображает.
Итог: Jinja даёт переменные, циклы с loop, условия и фильтры через |. Логика намеренно ограничена — тяжёлое остаётся в Python. Дальше — наследование шаблонов, чтобы не дублировать разметку.