Медиафайлы в Django: на что смотреть новичку
Загрузка файлов от пользователей ломается у новичков одинаково: картинки не отдаются, MEDIA не настроен, в проде статика раздаётся через Python. Разбираем, что настроить с самого начала.

Вы добавили поле ImageField в модель, загрузили аватарку через админку, а по ссылке видите 404. Или картинка открывается локально, но после деплоя пропадает. Это две самые частые проблемы новичка с медиафайлами в Django, и обе решаются правильной настройкой в самом начале. Разберём, что настроить, чтобы загруженные пользователями файлы сохранялись и отдавались. Актуально для Django 5.x, но настройки те же с версии 3.x.
Сначала о терминах, потому что их постоянно путают. Статика — это файлы, которые вы кладёте в проект сами: CSS, JavaScript, логотип сайта. Медиа — файлы, которые загружают пользователи через формы: аватары, документы, фото товаров. За них отвечают разные настройки, разные папки и разные механизмы раздачи. Здесь говорим только про медиа.
Что нужно иметь до начала
- Django 5.x установлен и проект запускается через
python manage.py runserver. - Модель, куда вы хотите сохранять файл — с полем
FileFieldилиImageField. - Для
ImageFieldнужна библиотека Pillow. Без неё Django выдаст ошибку при миграции. Ставится командойpip install Pillow. - Понимание, что
runserver— это дев-сервер. В проде медиа раздаёт не он.
Настройка за четыре шага
-
Задайте MEDIA_ROOT и MEDIA_URL в settings.py.
MEDIA_ROOT— это папка на диске, куда физически лягут файлы.MEDIA_URL— префикс в адресе, по которому они будут доступны.# settings.py (Django 5.x) from pathlib import Path BASE_DIR = Path(__file__).resolve().parent.parent # Папка на диске, куда сохраняются загруженные файлы MEDIA_ROOT = BASE_DIR / "media" # Префикс URL для доступа к этим файлам MEDIA_URL = "/media/"Проверка: после сохранения settings.py сервер перезапустится без ошибок.
-
Добавьте поле в модель и укажите upload_to. Параметр
upload_toзадаёт подпапку внутриMEDIA_ROOT. Без него все файлы свалятся в корень media одной кучей.# models.py from django.db import models class Profile(models.Model): name = models.CharField(max_length=100) # Файл ляжет в media/avatars/ avatar = models.ImageField(upload_to="avatars/", blank=True)Проверка: выполните
python manage.py makemigrationsиpython manage.py migrate— миграция создастся и применится без ошибок про Pillow. -
Подключите раздачу медиа в dev-режиме. Django не отдаёт медиафайлы сам по умолчанию. На время разработки это включается в корневом
urls.py. Именно здесь спотыкается большинство новичков: файл загрузился, а по ссылке 404.# urls.py (корневой) from django.conf import settings from django.conf.urls.static import static from django.contrib import admin from django.urls import path urlpatterns = [ path("admin/", admin.site.urls), ] # Раздача медиа работает только при DEBUG = True if settings.DEBUG: urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)Проверка: загрузите файл через админку, откройте объект и кликните по ссылке файла. Картинка должна открыться в браузере по адресу вида
/media/avatars/имя.jpg. -
Выведите файл в шаблоне через
.url. Не подставляйте путь руками — используйте атрибут поля.<!-- template.html --> {% if profile.avatar %} <img src="{{ profile.avatar.url }}" alt="{{ profile.name }}"> {% endif %}Проверка: аватар отображается на странице, а в исходном коде в атрибуте
srcстоит адрес с префиксом/media/.
Форма загрузки: одна деталь, без которой ничего не работает
Если вы принимаете файл через обычную форму, а не через админку, HTML-форма обязана иметь атрибут enctype="multipart/form-data". Без него файл просто не долетит до сервера, а поле окажется пустым — при этом ошибки не будет.
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
{{ form.as_p }}
<button type="submit">Сохранить</button>
</form>
И во вьюхе не забудьте передать request.FILES в форму, иначе Django не увидит загруженный файл:
# views.py
form = ProfileForm(request.POST, request.FILES)
Если не получилось
- По ссылке файла — 404 Not Found. Не подключена раздача медиа в
urls.py(шаг 3), либо файл физически не сохранился вMEDIA_ROOT. Проверьте, появилась ли папкаmedia/avatars/с файлом внутри. - Ошибка
Cannot use ImageField because Pillow is not installed. Установите Pillow:pip install Pillow, затем перезапустите сервер и повторите миграцию. - Форма отправляется, но поле файла пустое. Забыт
enctype="multipart/form-data"в теге form или не переданrequest.FILESво вьюхе. - Ошибка
SuspiciousFileOperationили странное имя файла. Обычно из-за небезопасного имени с кириллицей или спецсимволами. Django сам чистит имена, но при кастомной логике сохранения проверьте, что не собираете путь вручную из пользовательского ввода. - Медиа не отдаётся после деплоя. В проде
DEBUG = False, и блокstatic()из шага 3 отключается специально. Так и задумано — см. раздел ниже.
Почему в проде это работает иначе
Способ из шага 3 годится только для разработки. Django раздаёт файлы через Python-процесс, что медленно и небезопасно, поэтому при DEBUG = False этот механизм намеренно выключен. В проде медиа отдаёт веб-сервер — обычно Nginx — напрямую с диска, минуя Django.
Правило простое: код приложения обрабатывает загрузку и сохранение файла, а отдачу готового файла по сети делает Nginx. Смешивать эти задачи в одном процессе — путь к тормозам под нагрузкой.
Настройка Nginx для отдачи медиа — отдельная тема, здесь её не разворачиваем. Суть в том, что location /media/ указывается на путь, равный вашему MEDIA_ROOT, и Nginx отдаёт файлы сам.
MEDIA_ROOT в контейнере: частая ловушка
Если проект в Docker, папка media внутри контейнера исчезает при пересборке образа. Загруженные пользователями файлы пропадут. Решение — вынести MEDIA_ROOT на подключённый том (volume), который переживает пересборку. Это тоже про инфраструктуру, а не про код Django, но знать об этом стоит заранее.
Что делать дальше
Базовая загрузка работает — дальше стоит посмотреть на три вещи:
- Ограничение размера и типа файла. По умолчанию Django примет файл любого размера в рамках лимита, заданного настройкой размера тела запроса. Проверку расширения и веса добавляют валидаторами на поле формы.
- Хранение в облаке. Когда файлов станет много, локальный диск заменяют на S3-совместимое хранилище через библиотеку django-storages. Тогда
MEDIA_ROOTуже не нужен, а.urlначинает возвращать облачный адрес. - Уникальные имена файлов. Если два пользователя загрузят файл с одинаковым именем, Django добавит суффикс сам, но полагаться на это в бизнес-логике не стоит — задайте свою функцию в
upload_to.
Официальную справку по управлению файлами смотрите в документации Django для вашей версии — поведение upload_to и обработки файлов между 3.x и 5.x стабильно, но детали валидаторов уточняйте по версии.
Редактор изображений: Убрать лишнее, заменить фон, добавить объект, расширить кадр — на вашем изображении.
Спросить за 20 ₽Источники: Django documentation: Managing files, Django documentation: Deploying static files
Частые вопросы
У меня другая версия Django, например 4.2 — настройки те же?
Файл загрузился в админке, но по ссылке 404 — что не так?
В чём разница между статикой и медиа, если и то и другое — файлы?
Форма отправляется, но загруженный файл не сохраняется, ошибок нет — почему?
Почему после деплоя картинки перестали открываться?
Куда пропадают загруженные файлы после пересборки Docker-образа?
Материал носит информационный характер и подготовлен редакцией «Агентуры». Он не является офертой, рекламой или индивидуальной консультацией. Упомянутые продукты, компании и торговые знаки принадлежат их правообладателям. Перед принятием решений, влекущих юридические или финансовые последствия, обратитесь к профильному специалисту.