Загрузка нескольких файлов в Django: инструкция для новичков
Настроим форму с множественной загрузкой файлов в Django 5.x: HTML-атрибут multiple, обработка request.FILES.getlist и сохранение на диск. Займёт около 30 минут.

К концу этой инструкции у вас будет рабочая форма, через которую пользователь выбирает сразу несколько файлов и отправляет их одним запросом. Django примет их, проверит и сохранит в папку загрузок. Разберём самую частую точку провала новичка: request.FILES['file'] возвращает только один файл, даже если пользователь выбрал десять. Ниже — почему так и как получить весь список.
Проверено на Django 5.x и Python 3.11+. С Django 4.2 код работает так же — обработка нескольких файлов в ядре не менялась.
Что нужно до начала
- Установленный Django 5.x и созданный проект. Проверьте версию командой
python -m django --version— должна вывести 5.x.x. - Приложение в проекте, например
uploads, добавленное вINSTALLED_APPS. - Настроенные MEDIA_ROOT и MEDIA_URL в
settings.py— без них файлы некуда сохранять. - Понимание, что форма с файлами отправляется методом POST — GET файлы не передаёт.
Сразу добавьте в settings.py пути для загрузок, если их ещё нет:
# settings.py
import os
from pathlib import Path
BASE_DIR = Path(__file__).resolve().parent.parent
MEDIA_URL = '/media/'
MEDIA_ROOT = BASE_DIR / 'media'Проверка: после сохранения settings.py запустите python manage.py runserver — сервер должен стартовать без ошибок про неопределённые настройки.
Шаг 1. HTML-форма с атрибутом multiple
Ключевой момент — атрибут multiple у поля input type="file". Без него браузер даст выбрать только один файл. И обязателен enctype="multipart/form-data" у тега формы, иначе файлы вообще не долетят до сервера — придёт пустой request.FILES.
<!-- templates/uploads/upload.html -->
<form method="post" enctype="multipart/form-data">
{% csrf_token %}
<input type="file" name="files" multiple>
<button type="submit">Загрузить</button>
</form>Проверка: откройте страницу в браузере и нажмите на поле выбора файла. В диалоге операционной системы вы сможете выделить сразу несколько файлов, удерживая Ctrl или Shift.
Шаг 2. Получить весь список файлов во view
Вот где спотыкаются почти все. request.FILES['files'] вернёт только последний загруженный файл, потому что request.FILES — это MultiValueDict, и обращение по ключу отдаёт одно значение. Чтобы получить все файлы, нужен метод getlist.
# uploads/views.py
from django.shortcuts import render
from django.core.files.storage import FileSystemStorage
def upload_files(request):
saved = []
if request.method == 'POST':
# getlist возвращает список всех файлов с именем name="files"
files = request.FILES.getlist('files')
storage = FileSystemStorage()
for uploaded in files:
filename = storage.save(uploaded.name, uploaded)
saved.append(filename)
return render(request, 'uploads/upload.html', {'saved': saved})Проверка: добавьте в шаблон вывод {{ saved }} и загрузите два-три файла. После отправки на странице появится список сохранённых имён.
Запомните разницу:request.FILES['files']— один файл,request.FILES.getlist('files')— все. Имя'files'должно совпадать с атрибутомnameу поля формы.
Шаг 3. Подключить URL
Свяжите view с адресом, чтобы страница открывалась в браузере.
# uploads/urls.py
from django.urls import path
from . import views
urlpatterns = [
path('upload/', views.upload_files, name='upload_files'),
]# config/urls.py — корневой файл проекта
from django.contrib import admin
from django.urls import path, include
from django.conf import settings
from django.conf.urls.static import static
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('uploads.urls')),
]
# отдача media-файлов при разработке (DEBUG=True)
if settings.DEBUG:
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)Проверка: перейдите на http://127.0.0.1:8000/upload/ — откроется форма. Отдача файлов через static() работает только при DEBUG=True; в продакшене раздачей media занимается веб-сервер (Nginx или аналог).
Шаг 4. Простая проверка файлов перед сохранением
Не сохраняйте всё подряд. Ограничьте размер и разрешённые расширения — иначе первый же пользователь зальёт вам гигабайтный архив.
# uploads/views.py — дополненный цикл
MAX_SIZE = 5 * 1024 * 1024 # 5 МБ
ALLOWED_EXT = {'.jpg', '.jpeg', '.png', '.pdf'}
import os
for uploaded in files:
ext = os.path.splitext(uploaded.name)[1].lower()
if ext not in ALLOWED_EXT:
continue # пропускаем неразрешённый тип
if uploaded.size > MAX_SIZE:
continue # пропускаем слишком большой файл
filename = storage.save(uploaded.name, uploaded)
saved.append(filename)Проверка: попробуйте загрузить файл больше 5 МБ или, например, .exe — он не появится в списке сохранённых. Проверка расширения по имени — минимум; для реальной защиты проверяют ещё и содержимое файла, но это отдельная тема, здесь её не разворачиваем.
Если не получилось
- Сохраняется только один файл из нескольких. Вы используете
request.FILES['files']вместоrequest.FILES.getlist('files'). Замените обращение по ключу наgetlist. - request.FILES пустой, хотя файлы выбраны. У формы нет
enctype="multipart/form-data"либо форма отправляется методом GET. Проверьте тег<form>. - Ошибка
Forbidden (403) CSRF verification failed. В шаблоне нет{% csrf_token %}внутри формы. Добавьте его первой строкой после открывающего тега. - Ошибка
SuspiciousFileOperationили файл не появляется на диске. Не задан или неверныйMEDIA_ROOT, либо у процесса нет прав на запись в папку. Проверьте путь и права. - В браузере выбирается только один файл. У поля
inputнет атрибутаmultiple.
Что делать дальше
Рабочая форма готова. Дальше стоит перейти от ручного цикла к Django-форме: создайте forms.Form с полем forms.FileField и в Django 5.x укажите виджет forms.ClearableFileInput(attrs={'multiple': True}) — тогда валидацию каждого файла возьмёт на себя метод clean. Отдельно посмотрите, как связать загруженные файлы с моделью через FileField и ImageField, чтобы хранить не только файл на диске, но и запись в базе. В проде настройте раздачу MEDIA_ROOT веб-сервером и вынесите лимит размера запроса в DATA_UPLOAD_MAX_MEMORY_SIZE.
AI-стилист и фотограф: Замена фона, ретушь, аватарки, фото для резюме.
Спросить за 20 ₽Источники: Django docs: Managing files / Uploading multiple files, Django docs: QueryDict and MultiValueDict.getlist
Частые вопросы
Почему request.FILES['files'] возвращает только один файл?
А если у меня Django 4.2, код будет работать?
Файлы не сохраняются, а ошибок нет. Что проверить?
Как ограничить общий размер всех файлов сразу?
Достаточно ли проверять расширение файла для безопасности?
Файлы с одинаковыми именами перезаписывают друг друга?
Материал носит информационный характер и подготовлен редакцией «Агентуры». Он не является офертой, рекламой или индивидуальной консультацией. Упомянутые продукты, компании и торговые знаки принадлежат их правообладателям. Перед принятием решений, влекущих юридические или финансовые последствия, обратитесь к профильному специалисту.