Статические файлы: static, collectstatic, media

CSS, JavaScript, картинки, шрифты — это статика. Django помогает находить эти файлы в разработке и собирать их в одно место для продакшена.
Суть: тег {% static %} строит правильный URL к файлу. В разработке файлы отдаёт сам Django, в продакшене их собирают командой collectstatic и раздаёт веб-сервер.

Что такое статика и медиа

Файлы сайта делятся на два вида. Статика (static) — файлы, которые вы пишете как разработчик: стили, скрипты, логотипы, иконки. Они часть кода. Медиа (media) — файлы, которые загружают пользователи: аватары, вложения. Это разные понятия с разными настройками, и их часто путают.

Тег static

Никогда не пишите путь к статике вручную (/static/css/style.css). Используйте тег {% static %} — он построит правильный URL с учётом настроек и версионирования:

{% load static %}
<link rel="stylesheet" href="{% static 'blog/css/style.css' %}">
<img src="{% static 'blog/img/logo.png' %}" alt="Логотип">
<script src="{% static 'blog/js/app.js' %}"></script>

Не забудьте {% load static %} в начале шаблона — без него тег не работает.

Где лежат файлы

Как и с шаблонами, статику кладут в папку static/ внутри приложения, в подпапку с именем приложения: blog/static/blog/css/style.css. Django при APP_DIRS найдёт её автоматически. Ключевые настройки:

STATIC_URL = "static/"                  # префикс URL
STATICFILES_DIRS = [BASE_DIR / "static"] # общая статика проекта
STATIC_ROOT = BASE_DIR / "staticfiles"   # куда соберёт collectstatic

MEDIA_URL = "media/"                     # для пользовательских файлов
MEDIA_ROOT = BASE_DIR / "media"

Разработка против продакшена

Это место, где новички спотыкаются. В разработке (DEBUG=True) Django сам отдаёт статику из папок приложений — удобно. В продакшене Django не должен раздавать статику: это медленно и небезопасно. Вместо этого вы запускаете collectstatic, который собирает всю статику из всех приложений в одну папку STATIC_ROOT, и эту папку раздаёт Nginx или WhiteNoise.

python manage.py collectstatic

Как это работает под капотом

collectstatic — это, по сути, обход всех источников статики и копирование файлов в одну целевую папку с дедупликацией. Эту логику легко смоделировать:

# Попробуй сам ▶ — что делает collectstatic
sources = {
    "blog/static":  ["blog/css/style.css", "blog/img/logo.png"],
    "shop/static":  ["shop/css/cart.css"],
    "admin/static": ["admin/css/base.css", "admin/js/core.js"],
}

static_root = {}                 # одна общая папка staticfiles/
collected, skipped = 0, 0

for app, files in sources.items():
    for f in files:
        if f in static_root:     # уже скопирован из другого источника
            skipped += 1
            continue
        static_root[f] = app
        collected += 1

print(f"Собрано файлов: {collected}, пропущено дублей: {skipped}")
print("Содержимое STATIC_ROOT:")
for path, src in sorted(static_root.items()):
    print(f"  {path:24} ← {src}")

Django делает то же: проходит по всем приложениям и STATICFILES_DIRS, копирует файлы в STATIC_ROOT, а ManifestStaticFilesStorage ещё и добавляет хеш к именам для сброса кеша браузера.

WhiteNoise — простой вариант для продакшена

Для небольших проектов раздавать статику можно прямо из Python через библиотеку WhiteNoise — без отдельной настройки Nginx. Она добавляется как middleware и эффективно отдаёт собранную статику с правильными заголовками кеширования.

Частые ошибки

  • Хардкодить /static/ в путях. Используйте {% static %}.
  • Забыть {% load static %}. Тег не сработает.
  • Путать STATICFILES_DIRS и STATIC_ROOT. Первое — где искать, второе — куда собирать. Они не должны совпадать.
  • Ждать, что статика заработает на проде без collectstatic. При DEBUG=False Django её не отдаёт.
  • Путать static и media. Пользовательские загрузки — это media с отдельными настройками.

Best practices

  • Всегда обращайтесь к статике через {% static %}.
  • Раскладывайте статику по подпапкам приложений во избежание конфликтов имён.
  • На проде раздавайте статику через Nginx или WhiteNoise, не через Django.
  • Используйте ManifestStaticFilesStorage для версионирования и сброса кеша.

Итоги

Статика — ваши CSS/JS/картинки, медиа — загрузки пользователей. Тег {% static %} строит корректные URL. В разработке файлы отдаёт Django, в продакшене — collectstatic плюс веб-сервер. Не путайте STATICFILES_DIRS и STATIC_ROOT. Дальше перейдём к формам — главному способу получать данные от пользователя.

Проверьте себя
1. В чём разница между STATICFILES_DIRS и STATIC_ROOT?
AЭто синонимы
BDIRS — где Django ищет статику, ROOT — куда collectstatic её собирает
CROOT — для разработки, DIRS — для прода
DОба указывают на одну папку
2. Кто должен раздавать статику в продакшене?
AСам Django при DEBUG=True
BВеб-сервер (Nginx) или WhiteNoise после collectstatic
CБаза данных
DБраузер пользователя