Spec-Zone.ru › Django 6.0

Загрузка файлов

Когда Django обрабатывает загрузку файла, данные файла помещаются в request.FILES (подробнее об объекте request см. в документации по объектам запроса и ответа). В этом документе объясняется, как файлы хранятся на диске и в памяти, а также как настроить поведение по умолчанию.

Предупреждение

Принимая загружаемый контент от ненадёжных пользователей, вы подвергаетесь риску! Подробнее о мерах защиты см. в разделе руководства по безопасности, посвящённом контенту, загружаемому пользователями.

Основы загрузки файлов

Рассмотрим форму, содержащую поле FileField:

forms.py
from django import forms


class UploadFileForm(forms.Form):
    title = forms.CharField(max_length=50)
    file = forms.FileField()

Представление, обрабатывающее эту форму, получит данные файла в request.FILES — словаре, содержащем ключ для каждого поля FileField (или ImageField либо другого подкласса FileField) в форме. Таким образом, данные из приведённой выше формы будут доступны как request.FILES['file'].

Обратите внимание: request.FILES будет содержать данные, только если метод запроса — POST, было отправлено хотя бы одно поле для файла, а у <form>, отправившего запрос, есть атрибут enctype="multipart/form-data". В противном случае request.FILES будет пустым.

Чаще всего данные файла передаются из request в форму, как описано в разделе Привязка загруженных файлов к форме. Это выглядит примерно так:

views.py
from django.http import HttpResponseRedirect
from django.shortcuts import render
from .forms import UploadFileForm

# Imaginary function to handle an uploaded file.
from somewhere import handle_uploaded_file


def upload_file(request):
    if request.method == "POST":
        form = UploadFileForm(request.POST, request.FILES)
        if form.is_valid():
            handle_uploaded_file(request.FILES["file"])
            return HttpResponseRedirect("/success/url/")
    else:
        form = UploadFileForm()
    return render(request, "upload.html", {"form": form})

Обратите внимание, что нам нужно передать request.FILES конструктору формы: именно так данные файла привязываются к форме.

Вот распространённый способ обработки загруженного файла:

def handle_uploaded_file(f):
    with open("some/file/name.txt", "wb+") as destination:
        for chunk in f.chunks():
            destination.write(chunk)

Перебор UploadedFile.chunks() вместо использования read() гарантирует, что большие файлы не переполнят память системы.

У объектов UploadedFile есть и другие методы и атрибуты; полный справочник см. в разделе UploadedFile.

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

Если вы сохраняете файл в Model с помощью FileField, этот процесс значительно упрощается при использовании ModelForm. При вызове form.save() объект файла будет сохранён в расположении, указанном аргументом upload_to соответствующего поля FileField:

from django.http import HttpResponseRedirect
from django.shortcuts import render
from .forms import ModelFormWithFileField


def upload_file(request):
    if request.method == "POST":
        form = ModelFormWithFileField(request.POST, request.FILES)
        if form.is_valid():
            # file is saved
            form.save()
            return HttpResponseRedirect("/success/url/")
    else:
        form = ModelFormWithFileField()
    return render(request, "upload.html", {"form": form})

Если вы создаёте объект вручную, можно присвоить файловому полю модели объект файла из request.FILES:

from django.http import HttpResponseRedirect
from django.shortcuts import render
from .forms import UploadFileForm
from .models import ModelWithFileField


def upload_file(request):
    if request.method == "POST":
        form = UploadFileForm(request.POST, request.FILES)
        if form.is_valid():
            instance = ModelWithFileField(file_field=request.FILES["file"])
            instance.save()
            return HttpResponseRedirect("/success/url/")
    else:
        form = UploadFileForm()
    return render(request, "upload.html", {"form": form})

Если вы создаёте объект вручную вне запроса, файловому полю FileField можно присвоить объект типа File:

from django.core.management.base import BaseCommand
from django.core.files.base import ContentFile


class MyCommand(BaseCommand):
    def handle(self, *args, **options):
        content_file = ContentFile(b"Hello world!", name="hello-world.txt")
        instance = ModelWithFileField(file_field=content_file)
        instance.save()

Загрузка нескольких файлов

Чтобы загружать несколько файлов с помощью одного поля формы, создайте подкласс виджета этого поля и установите атрибут класса allow_multiple_selected в значение True.

Чтобы форма проверяла все такие файлы и включала их все в значение поля, необходимо также создать подкласс FileField. Пример приведён ниже.

Поле для нескольких файлов

Вероятно, в будущем в Django появится полноценная поддержка поля для нескольких файлов.

forms.py
from django import forms


class MultipleFileInput(forms.ClearableFileInput):
    allow_multiple_selected = True


class MultipleFileField(forms.FileField):
    def __init__(self, *args, **kwargs):
        kwargs.setdefault("widget", MultipleFileInput())
        super().__init__(*args, **kwargs)

    def clean(self, data, initial=None):
        single_file_clean = super().clean
        if isinstance(data, (list, tuple)):
            result = [single_file_clean(d, initial) for d in data]
        else:
            result = [single_file_clean(data, initial)]
        return result


class FileFieldForm(forms.Form):
    file_field = MultipleFileField()

Затем переопределите метод form_valid() подкласса FormView, чтобы обрабатывать загрузку нескольких файлов:

views.py
from django.views.generic.edit import FormView
from .forms import FileFieldForm


class FileFieldFormView(FormView):
    form_class = FileFieldForm
    template_name = "upload.html"  # Replace with your template.
    success_url = "..."  # Replace with your URL or reverse().

    def form_valid(self, form):
        files = form.cleaned_data["file_field"]
        for f in files:
            ...  # Do something with each file.
        return super().form_valid(form)

Предупреждение

Это позволит обрабатывать несколько файлов только на уровне формы. Обратите внимание: использовать это для добавления нескольких файлов в один экземпляр модели (в одно поле) нельзя, даже если пользовательский виджет используется с полем формы, связанным с моделью FileField.

Обработчики загрузки

Когда пользователь загружает файл, Django передаёт данные файла обработчику загрузки — небольшому классу, который обрабатывает данные файла по мере его загрузки. Обработчики загрузки изначально задаются в параметре FILE_UPLOAD_HANDLERS, значение которого по умолчанию:

[
    "django.core.files.uploadhandler.MemoryFileUploadHandler",
    "django.core.files.uploadhandler.TemporaryFileUploadHandler",
]

Вместе MemoryFileUploadHandler и TemporaryFileUploadHandler обеспечивают стандартное поведение Django при загрузке файлов: небольшие файлы считываются в память, а большие — на диск.

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

Где хранятся загруженные данные

Перед сохранением загруженных файлов данные необходимо где-то хранить.

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

Однако, если загруженный файл слишком велик, Django запишет его во временный файл в системном временном каталоге. В Unix-подобной системе Django создаст файл с именем, например, /tmp/tmpzfp6I6.upload. Если файл достаточно велик, можно наблюдать, как он увеличивается по мере того, как Django записывает данные на диск.

Эти параметры — 2,5 мегабайта, /tmp и т. д. — являются «разумными значениями по умолчанию», которые можно настроить, как описано в следующем разделе.

Изменение поведения обработчиков загрузки

Поведение Django при загрузке файлов контролируется несколькими настройками. Подробнее см. в разделе Настройки загрузки файлов.

Изменение обработчиков загрузки на лету

Иногда для отдельных представлений требуется изменить поведение загрузки. В таких случаях можно переопределить обработчики для конкретного запроса, изменив request.upload_handlers. По умолчанию этот список содержит обработчики загрузки, указанные в FILE_UPLOAD_HANDLERS, но его можно изменять так же, как и любой другой список.

Предположим, вы написали ProgressBarUploadHandler, который показывает ход загрузки в каком-либо AJAX-виджете. Добавить этот обработчик в список обработчиков загрузки можно так:

request.upload_handlers.insert(0, ProgressBarUploadHandler(request))

В этом случае, вероятно, нужно использовать list.insert() (вместо append()), поскольку обработчик индикатора выполнения должен запускаться до всех остальных обработчиков. Помните, что обработчики загрузки выполняются по порядку.

Чтобы полностью заменить обработчики загрузки, можно присвоить новый список:

request.upload_handlers = [ProgressBarUploadHandler(request)]

Примечание

Изменять обработчики загрузки можно только до обращения к request.POST или request.FILES — менять их после начала обработки загрузки не имеет смысла. Если попытаться изменить request.upload_handlers после чтения данных из request.POST или request.FILES, Django выдаст ошибку.

Поэтому изменяйте обработчики загрузки как можно раньше в представлении.

Кроме того, к request.POST обращается CsrfViewMiddleware, который включён по умолчанию. Это означает, что для изменения обработчиков загрузки необходимо применить к представлению csrf_exempt(). Затем нужно применить csrf_protect() к функции, которая фактически обрабатывает запрос. Обратите внимание: это означает, что обработчики могут начать получать загружаемый файл до выполнения проверок CSRF. Пример кода:

from django.views.decorators.csrf import csrf_exempt, csrf_protect


@csrf_exempt
def upload_file_view(request):
    request.upload_handlers.insert(0, ProgressBarUploadHandler(request))
    return _upload_file_view(request)


@csrf_protect
def _upload_file_view(request):
    # Process request
    ...

Если вы используете представление на основе класса, примените csrf_exempt() к его методу dispatch(), а csrf_protect() — к методу, который фактически обрабатывает запрос. Пример кода:

from django.utils.decorators import method_decorator
from django.views import View
from django.views.decorators.csrf import csrf_exempt, csrf_protect


@method_decorator(csrf_exempt, name="dispatch")
class UploadFileView(View):
    def setup(self, request, *args, **kwargs):
        request.upload_handlers.insert(0, ProgressBarUploadHandler(request))
        super().setup(request, *args, **kwargs)

    @method_decorator(csrf_protect)
    def post(self, request, *args, **kwargs):
        # Process request
        ...

© Django Software Foundation and individual contributors
Licensed under the BSD License.
https://docs.djangoproject.com/en/6.0/topics/http/file-uploads/

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API