Шаблоны, часть 1
Зачем нужны шаблоны
Возвращать HTML строкой из view неудобно:
def index(request):
return HttpResponse("<html><body><h1>Привет</h1>...</body></html>")
Шаблон — это HTML-файл со специальными вставками. View готовит данные, а шаблон решает, как их показать. Так разделяются логика и отображение (помните MTV?).
Где хранить шаблоны
По умолчанию (APP_DIRS = True в настройке TEMPLATES) Django ищет шаблоны в папке templates/ каждого приложения:
blog/
└── templates/
└── blog/
├── post_list.html
└── post_detail.html
Вложенная папка с именем приложения (templates/blog/) нужна, чтобы шаблоны разных приложений с одинаковыми именами не конфликтовали. В коде путь пишется как "blog/post_list.html".
Общие шаблоны (например, base.html) кладут в папку templates/ в корне проекта и указывают её в настройках:
TEMPLATES = [
{
"BACKEND": "django.template.backends.django.DjangoTemplates",
"DIRS": [BASE_DIR / "templates"],
"APP_DIRS": True,
...
}
]
render() и контекст
from django.shortcuts import render
def post_list(request):
posts = Post.objects.all()
return render(request, "blog/post_list.html", {"posts": posts, "title": "Блог"})
render(request, template_name, context) загружает шаблон, подставляет данные из словаря context и возвращает HttpResponse. Ключи словаря становятся переменными в шаблоне.
Переменные
<h1>{{ title }}</h1>
<p>Автор: {{ post.author.username }}</p>
<p>Первый пост: {{ posts.0.title }}</p>
<a href="{{ post.get_absolute_url }}">Читать</a>
Точка — универсальный доступ. Django по очереди пробует: ключ словаря → атрибут → индекс списка. Методы без аргументов вызываются автоматически (без скобок!). Несуществующая переменная выводится как пустая строка — без ошибки.
Автоэкранирование
Django автоматически экранирует HTML в переменных: если в comment.body окажется <script>, он выведется как текст, а не выполнится. Это защита от XSS-атак.
Теги: логика в шаблоне
Теги пишутся в {% %}.
Условия
{% if user.is_authenticated %}
<p>Привет, {{ user.username }}!</p>
{% elif show_banner %}
<p>Зарегистрируйтесь, чтобы комментировать.</p>
{% else %}
<a href="{% url 'login' %}">Войти</a>
{% endif %}
Доступны and, or, not, ==, !=, <, >, in:
{% if posts and not user.is_staff %}...{% endif %}
{% if post.status == "published" %}...{% endif %}
Циклы
<ul>
{% for post in posts %}
<li>{{ forloop.counter }}. {{ post.title }}</li>
{% empty %}
<li>Постов пока нет</li>
{% endfor %}
</ul>
Внутри цикла доступна переменная forloop:
| Переменная | Значение |
|---|---|
forloop.counter |
номер итерации с 1 |
forloop.counter0 |
номер с 0 |
forloop.first / forloop.last |
первая / последняя итерация |
forloop.revcounter |
сколько осталось |
Фильтры
Фильтры изменяют значение перед выводом, пишутся через |, аргумент — через ::
{{ name|upper }} ВАСЯ
{{ title|lower }} django
{{ body|truncatewords:30 }} первые 30 слов…
{{ body|linebreaks }} переносы строк → <p> и <br>
{{ created|date:"d.m.Y H:i" }} 25.10.2026 18:30
{{ price|floatformat:2 }} 199.90
{{ items|length }} 5
{{ bio|default:"Пока пусто" }} значение по умолчанию
{{ tags|join:", " }} django, python
{{ html_text|safe }} не экранировать (только для доверенного HTML!)
{{ title|title }} Каждое Слово С Большой
Фильтры можно комбинировать: {{ post.body|striptags|truncatewords:20 }}.
Комментарии
{# однострочный комментарий #}
{% comment "почему отключено" %}
<p>Этот блок не попадёт в HTML</p>
{% endcomment %}
Чего в шаблоне делать нельзя
Язык шаблонов Django намеренно ограничен: нельзя вызывать методы с аргументами, писать произвольный Python, обращаться к элементам через []. Это не баг, а принцип — логика должна жить во view и моделях, а шаблон только показывает данные.
Итог
- Шаблоны лежат в
app/templates/app/, общие — вtemplates/проекта. render(request, "путь.html", context)— основной способ вернуть HTML.{{ переменная }},{% тег %},{{ значение|фильтр:аргумент }},{# комментарий #}.- Переменные автоматически экранируются.
Практика решено 0 из 6
Как в шаблоне Django вывести значение переменной title?
Напишите view post_list, которая отрисовывает шаблон blog/post_list.html
и передаёт в контекст переменную posts со значением Post.objects.all().
from django.shortcuts import render
from .models import Post
def post_list(request):
posts = Post.objects.all()
return render(request, "blog/post_list.html", {"posts": posts})
Выведите список постов: для каждого post из posts — элемент <li> с {{ post.title }}.
Если постов нет — <li>Постов пока нет</li> (используйте {% empty %}).
<ul>
{% for post in posts %}
<li>{{ post.title }}</li>
{% empty %}
<li>Постов пока нет</li>
{% endfor %}
</ul>
Что выведет {{ "привет мир"|title }}?
Переменная body содержит длинный текст. Какой фильтр (с аргументом) оставит из него только первые 30 слов?
Напишите выражение целиком, например {{ body|... }}.
Фильтр называется truncate…, а аргумент передаётся через двоеточие.
{{ body|truncatewords:30 }}Какие выражения в шаблоне Django корректны?
Войдите, чтобы отмечать пройденные уроки.