Шаблоны и статика в Django 5.x: пошаговая настройка
Новый проект отдаёт голый HTML без стилей, а браузер ругается на 404 по CSS. Разбираем, как правильно подключить шаблоны и статические файлы в Django 5.x с нуля.

К концу этой инструкции у вас будет страница, которую Django рендерит из HTML-шаблона, с подключёнными CSS и картинкой из папки статики. Займёт 15–20 минут. Ошибки вроде TemplateDoesNotExist и битых стилей после этого исчезнут, потому что вы будете понимать, где Django ищет файлы и почему.
Django разделяет два разных потока файлов: шаблоны (HTML, который движок собирает на сервере) и статику (CSS, JS, изображения, которые браузер забирает как есть). Настраиваются они по-разному, и путать их — источник половины проблем у новичков.
Что нужно до начала
Инструкция написана для Django 5.x (проверено на 5.0 и 5.1). На более старых 4.2 всё работает так же, отличий в этой части нет.
- Установленный Python 3.10+ и Django 5.x (
pip install django). - Созданный проект командой
django-admin startproject mysite. - Внутри проекта — созданное приложение:
python manage.py startapp blog. - Приложение
blogдобавлено вINSTALLED_APPSвsettings.py. Без этого шага автопоиск шаблонов и статики внутри приложения работать не будет.
Дальше все пути указаны относительно корня проекта — той папки, где лежит manage.py.
Шаг 1. Проверяем настройки шаблонов
Откройте settings.py и найдите список TEMPLATES. По умолчанию startproject создаёт такой блок:
TEMPLATES = [
{
"BACKEND": "django.template.backends.django.DjangoTemplates",
"DIRS": [],
"APP_DIRS": True,
"OPTIONS": {
"context_processors": [
"django.template.context_processors.request",
"django.contrib.auth.context_processors.auth",
"django.contrib.messages.context_processors.messages",
],
},
},
]Ключевая строка — "APP_DIRS": True. При таком значении Django сам ищет шаблоны в папке templates/ внутри каждого приложения из INSTALLED_APPS. Ничего добавлять не нужно, если храните шаблоны внутри приложений. Это исходное значение, менять его не надо.
Шаг 2. Создаём шаблон
Внутри приложения blog создайте вложенные папки и файл: blog/templates/blog/index.html.
Обратите внимание на двойную вложенность: папка blog внутри templates. Это не опечатка. Django складывает шаблоны всех приложений в одно пространство имён, и вложенная папка не даёт файлам с одинаковым именем перезаписать друг друга. Если у двух приложений будет index.html прямо в templates/, Django возьмёт первый попавшийся.
Содержимое index.html пока минимальное:
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="utf-8">
<title>Главная</title>
</head>
<body>
<h1>Шаблон работает</h1>
</body>
</html>Шаг 3. Отдаём шаблон через view
В blog/views.py опишите функцию, которая рендерит шаблон:
from django.shortcuts import render
def index(request):
# Первый аргумент render — путь к шаблону от корня папки templates
return render(request, "blog/index.html")Теперь подключите view к URL. Создайте blog/urls.py:
from django.urls import path
from . import views
urlpatterns = [
path("", views.index, name="index"),
]И подключите его в главном mysite/urls.py:
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path("admin/", admin.site.urls),
path("", include("blog.urls")),
]Запустите сервер: python manage.py runserver. По адресу http://127.0.0.1:8000/ должна открыться страница с заголовком «Шаблон работает». Если так — шаблоны подключены.
Шаг 4. Настраиваем статику
В settings.py проверьте, что задан STATIC_URL — startproject создаёт его автоматически:
STATIC_URL = "static/"Это URL-префикс, по которому браузер будет запрашивать статику: например, /static/blog/style.css. Как и с шаблонами, при включённых приложениях Django ищет статику в папке static/ внутри каждого приложения. Отдельно настраивать поиск в режиме разработки не нужно.
Создайте файл blog/static/blog/style.css с двойной вложенностью по той же причине, что и у шаблонов:
body {
font-family: sans-serif;
background: #f4f4f4;
}
h1 {
color: #2b6cb0;
}Шаг 5. Подключаем статику в шаблоне
Django не даёт писать путь к CSS вручную — за это отвечает шаблонный тег {% static %}. Он собирает правильный URL и не сломается, когда вы поменяете STATIC_URL или переедете на CDN.
Перепишите index.html. Тег {% load static %} обязателен в самом верху файла — без него {% static %} выдаст ошибку.
{% load static %}
<!DOCTYPE html>
<html lang="ru">
<head>
<meta charset="utf-8">
<title>Главная</title>
<link rel="stylesheet" href="{% static 'blog/style.css' %}">
</head>
<body>
<h1>Стили подключены</h1>
</body>
</html>Перезагрузите страницу в браузере. Заголовок должен стать синим, фон — светло-серым. Откройте инструменты разработчика, вкладку Network: запрос style.css должен вернуть код 200, а не 404.
Главное правило: путь внутри{% static %}отсчитывается не от корня проекта, а от папкиstatic/. Файл лежит вblog/static/blog/style.css— значит в теге пишете'blog/style.css', безstaticв начале.
Если не получилось
Четыре ошибки, которые встречаются чаще всего.
- TemplateDoesNotExist: blog/index.html. Django не нашёл шаблон. Проверьте двойную вложенность (
blog/templates/blog/index.html) и чтоblogесть вINSTALLED_APPS. ПриAPP_DIRS: Trueбез записи вINSTALLED_APPSпоиск не сработает. - Invalid block tag 'static'. Did you forget to register or load this tag? Забыли
{% load static %}в начале шаблона. Тег нужно загружать в каждом файле, где он используется. - CSS даёт 404 в Network. Проверьте путь в теге и реальное расположение файла. Часто причина — файл лежит в
blog/static/style.cssбез вложенной папкиblog, а в теге написано'blog/style.css'. Пути должны совпадать. - Стили не меняются после правок. Браузер закешировал старый CSS. Обновите страницу с
Ctrl+F5(илиCmd+Shift+Rна Mac).
Что делать дальше: статика на проде
Всё выше работает только при DEBUG = True — во время разработки Django сам раздаёт статику. На боевом сервере он этого делать не будет, и файлы придётся собрать в одну папку.
Для этого в settings.py задают STATIC_ROOT — папку, куда команда python manage.py collectstatic скопирует статику из всех приложений. Отдавать эту папку должен веб-сервер (Nginx) или пакет вроде WhiteNoise, а не сам Django. Настройка боевой раздачи — отдельная тема; здесь важно запомнить, что STATIC_ROOT и STATIC_URL — разные вещи: первое про папку на диске, второе про URL для браузера.
Точный порядок настройки collectstatic и разницу режимов смотрите в официальной документации по управлению статикой.
Логотип и фирменный стиль: Три концепции разного характера, палитра с обоснованием, проверка на читаемость в малом размере и в один цвет.
Спросить за 60 ₽Источники: Django docs: Managing static files, Django docs: Templates
Частые вопросы
У меня Django 4.2, а не 5.x — инструкция подойдёт?
Куда класть шаблоны, если они общие для нескольких приложений?
Почему CSS не подключается, хотя путь вроде правильный?
Нужно ли настраивать STATICFILES_DIRS?
Статика работает локально, но пропала на сервере — что не так?
Материал носит информационный характер и подготовлен редакцией «Агентуры». Он не является офертой, рекламой или индивидуальной консультацией. Упомянутые продукты, компании и торговые знаки принадлежат их правообладателям. Перед принятием решений, влекущих юридические или финансовые последствия, обратитесь к профильному специалисту.