123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356 |
- ============
- File Uploads
- ============
- .. currentmodule:: django.core.files.uploadedfile
- When Django handles a file upload, the file data ends up placed in
- :attr:`request.FILES <django.http.HttpRequest.FILES>` (for more on the
- ``request`` object see the documentation for :doc:`request and response objects
- </ref/request-response>`). This document explains how files are stored on disk
- and in memory, and how to customize the default behavior.
- .. warning::
- There are security risks if you are accepting uploaded content from
- untrusted users! See the security guide's topic on
- :ref:`user-uploaded-content-security` for mitigation details.
- Basic file uploads
- ==================
- Consider a form containing a :class:`~django.forms.FileField`:
- .. code-block:: python
- :caption: ``forms.py``
- from django import forms
- class UploadFileForm(forms.Form):
- title = forms.CharField(max_length=50)
- file = forms.FileField()
- A view handling this form will receive the file data in
- :attr:`request.FILES <django.http.HttpRequest.FILES>`, which is a dictionary
- containing a key for each :class:`~django.forms.FileField` (or
- :class:`~django.forms.ImageField`, or other :class:`~django.forms.FileField`
- subclass) in the form. So the data from the above form would
- be accessible as ``request.FILES['file']``.
- Note that :attr:`request.FILES <django.http.HttpRequest.FILES>` will only
- contain data if the request method was ``POST``, at least one file field was
- actually posted, and the ``<form>`` that posted the request has the attribute
- ``enctype="multipart/form-data"``. Otherwise, ``request.FILES`` will be empty.
- Most of the time, you'll pass the file data from ``request`` into the form as
- described in :ref:`binding-uploaded-files`. This would look something like:
- .. code-block:: python
- :caption: ``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})
- Notice that we have to pass :attr:`request.FILES <django.http.HttpRequest.FILES>`
- into the form's constructor; this is how file data gets bound into a form.
- Here's a common way you might handle an uploaded file::
- def handle_uploaded_file(f):
- with open("some/file/name.txt", "wb+") as destination:
- for chunk in f.chunks():
- destination.write(chunk)
- Looping over ``UploadedFile.chunks()`` instead of using ``read()`` ensures that
- large files don't overwhelm your system's memory.
- There are a few other methods and attributes available on ``UploadedFile``
- objects; see :class:`UploadedFile` for a complete reference.
- Handling uploaded files with a model
- ------------------------------------
- If you're saving a file on a :class:`~django.db.models.Model` with a
- :class:`~django.db.models.FileField`, using a :class:`~django.forms.ModelForm`
- makes this process much easier. The file object will be saved to the location
- specified by the :attr:`~django.db.models.FileField.upload_to` argument of the
- corresponding :class:`~django.db.models.FileField` when calling
- ``form.save()``::
- 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})
- If you are constructing an object manually, you can assign the file object from
- :attr:`request.FILES <django.http.HttpRequest.FILES>` to the file field in the
- model::
- 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})
- If you are constructing an object manually outside of a request, you can assign
- a :class:`~django.core.files.File` like object to the
- :class:`~django.db.models.FileField`::
- 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()
- .. _uploading_multiple_files:
- Uploading multiple files
- ------------------------
- ..
- Tests in tests.forms_tests.field_tests.test_filefield.MultipleFileFieldTest
- should be updated after any changes in the following snippets.
- If you want to upload multiple files using one form field, create a subclass
- of the field's widget and set its ``allow_multiple_selected`` class attribute
- to ``True``.
- In order for such files to be all validated by your form (and have the value of
- the field include them all), you will also have to subclass ``FileField``. See
- below for an example.
- .. admonition:: Multiple file field
- Django is likely to have a proper multiple file field support at some point
- in the future.
- .. code-block:: python
- :caption: ``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()
- Then override the ``form_valid()`` method of your
- :class:`~django.views.generic.edit.FormView` subclass to handle multiple file
- uploads:
- .. code-block:: python
- :caption: ``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)
- .. warning::
- This will allow you to handle multiple files at the form level only. Be
- aware that you cannot use it to put multiple files on a single model
- instance (in a single field), for example, even if the custom widget is used
- with a form field related to a model ``FileField``.
- Upload Handlers
- ===============
- .. currentmodule:: django.core.files.uploadhandler
- When a user uploads a file, Django passes off the file data to an *upload
- handler* -- a small class that handles file data as it gets uploaded. Upload
- handlers are initially defined in the :setting:`FILE_UPLOAD_HANDLERS` setting,
- which defaults to::
- [
- "django.core.files.uploadhandler.MemoryFileUploadHandler",
- "django.core.files.uploadhandler.TemporaryFileUploadHandler",
- ]
- Together :class:`MemoryFileUploadHandler` and
- :class:`TemporaryFileUploadHandler` provide Django's default file upload
- behavior of reading small files into memory and large ones onto disk.
- You can write custom handlers that customize how Django handles files. You
- could, for example, use custom handlers to enforce user-level quotas, compress
- data on the fly, render progress bars, and even send data to another storage
- location directly without storing it locally. See :ref:`custom_upload_handlers`
- for details on how you can customize or completely replace upload behavior.
- Where uploaded data is stored
- -----------------------------
- Before you save uploaded files, the data needs to be stored somewhere.
- By default, if an uploaded file is smaller than 2.5 megabytes, Django will hold
- the entire contents of the upload in memory. This means that saving the file
- involves only a read from memory and a write to disk and thus is very fast.
- However, if an uploaded file is too large, Django will write the uploaded file
- to a temporary file stored in your system's temporary directory. On a Unix-like
- platform this means you can expect Django to generate a file called something
- like ``/tmp/tmpzfp6I6.upload``. If an upload is large enough, you can watch this
- file grow in size as Django streams the data onto disk.
- These specifics -- 2.5 megabytes; ``/tmp``; etc. -- are "reasonable defaults"
- which can be customized as described in the next section.
- Changing upload handler behavior
- --------------------------------
- There are a few settings which control Django's file upload behavior. See
- :ref:`File Upload Settings <file-upload-settings>` for details.
- .. _modifying_upload_handlers_on_the_fly:
- Modifying upload handlers on the fly
- ------------------------------------
- Sometimes particular views require different upload behavior. In these cases,
- you can override upload handlers on a per-request basis by modifying
- ``request.upload_handlers``. By default, this list will contain the upload
- handlers given by :setting:`FILE_UPLOAD_HANDLERS`, but you can modify the list
- as you would any other list.
- For instance, suppose you've written a ``ProgressBarUploadHandler`` that
- provides feedback on upload progress to some sort of AJAX widget. You'd add this
- handler to your upload handlers like this::
- request.upload_handlers.insert(0, ProgressBarUploadHandler(request))
- You'd probably want to use ``list.insert()`` in this case (instead of
- ``append()``) because a progress bar handler would need to run *before* any
- other handlers. Remember, the upload handlers are processed in order.
- If you want to replace the upload handlers completely, you can assign a new
- list::
- request.upload_handlers = [ProgressBarUploadHandler(request)]
- .. note::
- You can only modify upload handlers *before* accessing
- ``request.POST`` or ``request.FILES`` -- it doesn't make sense to
- change upload handlers after upload handling has already
- started. If you try to modify ``request.upload_handlers`` after
- reading from ``request.POST`` or ``request.FILES`` Django will
- throw an error.
- Thus, you should always modify uploading handlers as early in your view as
- possible.
- Also, ``request.POST`` is accessed by
- :class:`~django.middleware.csrf.CsrfViewMiddleware` which is enabled by
- default. This means you will need to use
- :func:`~django.views.decorators.csrf.csrf_exempt` on your view to allow you
- to change the upload handlers. You will then need to use
- :func:`~django.views.decorators.csrf.csrf_protect` on the function that
- actually processes the request. Note that this means that the handlers may
- start receiving the file upload before the CSRF checks have been done.
- Example code::
- 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
- ...
- If you are using a class-based view, you will need to use
- :func:`~django.views.decorators.csrf.csrf_exempt` on its
- :meth:`~django.views.generic.base.View.dispatch` method and
- :func:`~django.views.decorators.csrf.csrf_protect` on the method that
- actually processes the request. Example code::
- 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
- ...
|