Шаблоны, часть 1

⏱ 20 мин · 🧩 заданий: 6

Зачем нужны шаблоны

Возвращать 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

Вы не вошли — ответы проверяются, но прогресс не сохранится. Войти или зарегистрироваться.
Задание 1 Один вариант ответа

Как в шаблоне Django вывести значение переменной title?

Задание 2 Написать код

Напишите view post_list, которая отрисовывает шаблон blog/post_list.html и передаёт в контекст переменную posts со значением Post.objects.all().

Задание 3 Написать код

Выведите список постов: для каждого post из posts — элемент <li> с {{ post.title }}. Если постов нет — <li>Постов пока нет</li> (используйте {% empty %}).

Задание 4 Один вариант ответа

Что выведет {{ "привет мир"|title }}?

Задание 5 Ввод ответа

Переменная body содержит длинный текст. Какой фильтр (с аргументом) оставит из него только первые 30 слов? Напишите выражение целиком, например {{ body|... }}.

Задание 6 Несколько вариантов

Какие выражения в шаблоне Django корректны?

Войдите, чтобы отмечать пройденные уроки.