Перейти к содержанию

Django: базовый шаблон и статические файлы

Оглавление курса · Скачать все состояния проекта.

Учебная задача: Собрать общий HTML-документ через наследование шаблонов и подключить CSS приложения.

Входное состояние: Снимок lesson-03: самостоятельный HTML списка. Выходное состояние: Базовый шаблон, блок content, именованная ссылка и namespaced CSS. Эта глава продолжает «Редакционный стол» — небольшой сайт заметок. Нужны основы Python, HTML и устройство HTTP-запроса. Используем Python 3.12 и Django 5.2.18 LTS; номер ветки выбран для этой серии. Полный каталог lesson-04 в архиве содержит собственные настройки, шаблоны и историю миграций. При самостоятельном прохождении выберите один снимок и отдельную локальную базу; смешивание файлов разных глав меняет договор примера.

Исходники подготовлены редакционным чтением. Зависимости, миграции, сервер и команды работы с данными здесь не выполнялись. Все описанные ответы и изменения являются ожидаемыми, example_execution: not_run.

Список заметок уже получает данные из представления. Теперь подготовим общую оболочку для списка, карточки и форм. Если копировать head, шапку и подключение CSS в каждый файл, любое изменение потребует нескольких правок. Наследование шаблонов делает общий документ единым, а отдельная страница заполняет определённые области. При этом итог для браузера остаётся обычным HTML: браузер ничего не знает о конструкции extends и не выполняет Django-теги.

В проекте используем базовый шаблон templates/base.html. Каталог указан в DIRS настройки шаблонов, поэтому Django может найти файл вне конкретного приложения. В нём размещены язык документа, кодировка, viewport, шапка и основная область. Шаблон списка оставляет только заголовок и цикл заметок. Такое распределение удобно уже сейчас и станет существенным, когда появится вход автора: одно изменение общей шапки затронет все наследующие страницы.

Блоки наследования

Тег extends сообщает, какой шаблон является родительским. Блок content в дочернем шаблоне заменяет одноимённый блок родителя. В базовом файле можно оставить пустое значение, потому что каждая страница даст собственное содержимое. Блок title имеет значение по умолчанию — «Редакционный стол». Позднее карточка переопределит его названием заметки. Так отсутствие переопределения остаётся корректным случаем, а не ошибкой документа.

Не размещайте второй <html> в дочернем шаблоне. Он попадёт внутрь уже существующего документа и нарушит структуру. При чтении результата мысленно замените блок родителя содержимым дочернего файла. Получится ровно один head, один body и один main. Этот простой приём помогает заметить лишние обёртки до запуска. Наследование предназначено для композиции документа, а не для вложения целых HTML-страниц друг в друга.

Ссылка шапки строится через {% url 'notes:list' %}. Её значение будет получено по имени маршрута из предыдущего урока. Если адрес списка изменится в URLconf, шаблон продолжит обращаться к тому же имени. Обратите внимание на кавычки вокруг имени: это строковое имя маршрута. Без кавычек язык шаблона станет искать значение переменной. Ошибка построения ссылки обычно означает несогласованное имя, пространство имён или недостающий аргумент, а не неправильную работу HTML.

Откуда берётся CSS

Статические файлы — это ресурсы приложения, которые не создаются из пользовательского ввода на каждом запросе. К ним относятся CSS, JavaScript и изображения оформления. Файл notes/static/notes/desk.css имеет собственный префикс notes, как и шаблон списка. Это защищает от столкновения с CSS другого приложения с похожим именем. В шаблоне сначала загружаем библиотеку static, затем строим адрес через тег {% static 'notes/desk.css' %}.

Адрес ресурса и путь на диске различаются. STATIC_URL задаёт публичный префикс, STATIC_ROOT — место для собранных ресурсов выпуска. Каталог static внутри приложения является источником. Не копируйте вручную исходный CSS в STATIC_ROOT при каждой правке урока: позже система сборки статических файлов соединит ресурсы приложений. В локальной разработке обслуживание отличается от выпуска, поэтому успешное получение CSS локально ещё не описывает настройку производственного сервера.

Наш CSS задаёт читабельную ширину, поля, фон карточек и видимый фокус. Ширина ограничена относительно размера шрифта, а не рассчитана для одного монитора. Изображение ограничено шириной контейнера, хотя обложки ещё нет: общее правило пригодится следующей части курса. Элементы формы наследуют шрифт. Это небольшая оболочка, помогающая читать данные; урок не требует превращать сайт в законченный дизайн-сервис.

Документ после объединения

Ожидается общая шапка со ссылкой на список, затем два блока заметок внутри main. Если CSS недоступен, содержание всё равно должно читаться и ссылки должны работать. Такое разделение полезно для поиска причин: неправильные значения заметок относятся к контексту, отсутствие стилей — к ресурсу, повторившаяся шапка — к структуре наследования. Не исправляйте данные ради визуального сбоя и не меняйте маршруты ради отступов.

Проверьте отличие загрузки библиотеки тегов от наследования. load static позволяет использовать тег static в конкретном шаблоне; extends выбирает родительский документ. Дочерний шаблон не обязан повторять подключение CSS, если оно уже находится в родителе. Если вы добавите новый статический ресурс непосредственно в дочерний файл, загрузка библиотеки тегов должна быть доступна там, где ресурс используется. Эти операции решают разные задачи.

Для самостоятельной работы добавьте в родительский шаблон небольшой футер с названием редакции. Предскажите, где он окажется в списке и в будущей карточке. Затем задайте другое значение блока title у списка, сохранив общий заголовок в шапке. Название вкладки и видимый заголовок страницы не обязаны быть одной строкой. В следующей главе займёмся хранением заметок: вёрстка останется той же, а данные перестанут быть временными словарями.

Разбор связи файлов

Блок title меняет заголовок вкладки, а блок content — основную область документа. Наследование не добавляет второй документ внутрь первого. Дочерний файл объявляет, какие части базовой структуры следует заменить. Если скопировать в него собственные html и body вокруг блоков, получатся лишние элементы и менее понятная структура. Полный файл в примере показывает нужный уровень вложенности.

У статики есть пространство имён каталога notes. Без него два приложения с одинаковым именем style.css могли бы конкурировать при поиске файла. Это не то же самое, что пространство имён URL: одно упорядочивает ресурсы оформления, другое имена маршрутов. В базовом шаблоне тег static строит публичный адрес, поэтому не пишем путь к каталогу исходников вручную.

Если ожидаемый стиль не появился, разделите два вопроса: найден ли ресурс и содержит ли он нужное правило. Изменение цвета в CSS не исправит неверный адрес файла, а повторное копирование шаблона не исправит неподходящий селектор. Учебное состояние имеет простую шапку, ограниченную ширину текста и заметный фокус ссылок. Такой результат помогает читать материал и пользоваться клавиатурой; дальнейшее усложнение оформления не требуется для понимания механизма наследования.

Полные файлы этого шага

Следующие файлы показаны полностью. Остальные согласованные файлы находятся в том же снимке архива; переносите весь каталог при выборе исходного состояния.

templates/base.html

{% load static %}
<!doctype html>
<html lang="ru">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>
{% block title %}Редакционный стол{% endblock %}
</title>
<link rel="stylesheet" href="{% static 'notes/desk.css' %}">
</head>
<body>
<header>
<a href="{% url 'notes:list' %}">Редакционный стол</a>
</header>
<main>
{% block content %}{% endblock %}
</main>
</body>
</html>

notes/templates/notes/list.html

{% extends "base.html" %}
{% block content %}
<h1>Заметки редакции</h1>
{% for note in notes %}
<article>
<h2>{{ note.title }}</h2>
<p>{{ note.body }}</p>
</article>
{% empty %}
<p>Заметок пока нет.</p>
{% endfor %}{% endblock %}

notes/static/notes/desk.css

body { font-family: system-ui,sans-serif; color:#1d2f40; background:#f2f5f8; margin:0; }
header,main { max-width:54rem; margin:auto; padding:1.5rem; }
article,form { padding:1rem; margin:1rem 0; background:white; border:1px solid #ccd7e1; }
a { color:#0753a5; } input,textarea,select,button { font:inherit; max-width:100%; }
label { display:block; } img { max-width:100%; height:auto; } .errorlist { color:#a51b28; }
:focus-visible { outline:3px solid #df8300; outline-offset:3px; }

Документация и исходники

Механизм этой главы описан в официальной документации Django 5.2. При самостоятельной проверке сначала сопоставьте маршрут, представление и состояние данных, затем сравните результат с указанным ожидаемым поведением. Архив учебного проекта содержит все шестнадцать самостоятельных этапов и договор эксплуатации.