ファイルのアップロードLink to this heading
Django がファイルアップロードを扱うとき、ファイルデータは request.FILES に格納されます (request についての詳細は request and response objects をご覧ください)。ここでは、ファイルがどのようにディスクとメモリに保管され、またどうやってデフォルトの動作を変更するかを説明します。
ファイルのアップロードの基本Link to this heading
Consider a form containing a FileField:
forms.pyfrom django import forms
class UploadFileForm(forms.Form):
title = forms.CharField(max_length=50)
file = forms.FileField()
このフォームをハンドリングするビューは、ファイルのデータを request.FILES というディクショナリの中に受け取ります。このディクショナリには、キーと、それぞれのキーに対応するフォームの FileField (または ImageField または他の FileField のサブクラス) が格納されています。そのため、上のフォームから送信されたデータには、request.FILES['file'] でアクセスすることができます。
Note that request.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 Binding uploaded files to a form. This would look something like:
views.pyfrom 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)
read() を使う代わりに UploadedFile.chunks() でループすることで、大きなサイズのファイルがアップロードされた時にメモリが専有されることを防げます。
UploadedFile オブジェクトには、他にもいくつかのメソッドと属性があります。完全なリファレンスについては、UploadedFile を読んでください。
モデルを使用したアップロードファイルのハンドリングLink to this heading
FileField を持つ Model 上のファイルを保存するときは、ModelForm を使えば、このプロセスはとても簡単になります。この場合、ファイルオブジェクトは、form.save() を呼び出すだけで、対応する FileField の upload_to 引数で指定した場所に保存されます。
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
request.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 File like object to the
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()
複数のファイルをアップロードするLink to this heading
If you want to upload multiple files using one form field, create a subclass
of the field's widget and set the allow_multiple_selected attribute on it
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.
forms.pyfrom 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()
そして、複数のファイルアップロードを扱うために、あなたの FormView サブクラスの post メソッドをオーバーライドします:
views.pyfrom 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 post(self, request, *args, **kwargs):
form_class = self.get_form_class()
form = self.get_form(form_class)
if form.is_valid():
return self.form_valid(form)
else:
return self.form_invalid(form)
def form_valid(self, form):
files = form.cleaned_data["file_field"]
for f in files:
... # Do something with each file.
return super().form_valid()
アップロードハンドラLink to this heading
サイトのユーザがファイルをアップロードした時、Django はそのファイルのデータを アップロードハンドラ (upload handler) という、ファイルがアップロードされた時にデータをハンドリングするための小さなクラスへ渡します。アップロードハンドラは、初めに設定の FILE_UPLOAD_HANDLERS で定義されています。デフォルトでは次のようになっています。
[
"django.core.files.uploadhandler.MemoryFileUploadHandler",
"django.core.files.uploadhandler.TemporaryFileUploadHandler",
]
MemoryFileUploadHandler と TemporaryFileUploadHandler はともに、Django でファイルがアップロードされた時のデフォルトの動作――小さなファイルはメモリ上に保存し、大きなファイルはディスクに保存する――を提供しています。
カスタマイズハンドラを書けば、Django がファイルをハンドリングする方法をカスタマイズすることができます。たとえば、カスタムハンドラを使うことで、ユーザレベルでのクオータを設定したり、データをその場で圧縮したり、プログレスバーを表示したり、あるいは、送られたデータをローカルに保存せずに、別のストレージに転送することさえ可能です。ハンドラをカスタマイズしたり、アップロード時の動作を完全に置き換えたりする方法については、詳しくは アップロードハンドラをカスタマイズする を読んでください。
アップロードされたデータの保存場所Link to this heading
アップロードされたファイルを保存する時点で、そのデータはコンピュータ上のどこかに保存されているはずです。
デフォルトでは、アップロードファイルが 2.5 MB 未満ならば、Django はデータ全体をメモリ上に保持します。つまり、この場合にファイルを保存するというのは、メモリ上からデータを読み込んでディスクにファイルを書き込むだけなので、処理は短時間しかかかりません。
しかし、アップロードファイルのサイズが大きい場合、Django はアップロードファイルをシステムの一時ディレクトリ内に、一時ファイルとして保存します。Unix-like なプラットフォームなら、Django は /tmp/tmpzfp6I6.upload のようなファイルを作成すると考えて良いです。アップロードされたファイルが十分大きければ、Django がデータストリームをディスクに書き込むにつれ、このファイルのサイズが大きくなってゆくのを観察することができるでしょう。
These specifics -- 2.5 megabytes; /tmp; etc. -- are "reasonable defaults"
which can be customized as described in the next section.
アップロードハンドラの動作の変更Link to this heading
Django のファイルアップロードの動作を制御するための設定がいくつかあります。詳しくは、 File Upload Settings を参照してください。
その場でアップロードハンドラを修正するLink to this heading
時として特定のビューが異なるアップロード動作を必要とすることがあります。このような場合には、request.upload_handlers を修正することで、1リクエストごとに、アップロードハンドラをオーバーライドすることが可能です。デフォルトでは、このリストには FILE_UPLOAD_HANDLERS の設定で指定したアップロードハンドラが入っていますが、このリストは自由に修正することができます。
たとえば、アップロードの進行状況を計算して、AJAX のウィジェットなどにフィードバックを返す ProgressBarUploadHandler というハンドラを作ったとしましょう。このハンドラを、次のようにアップロードハンドラのリストに追加します。
request.upload_handlers.insert(0, ProgressBarUploadHandler(request))
この場合には、list.append() メソッドの代わりに list.insert() を使ったほうが良いでしょう。なぜなら、プログレスバーハンドラーは、他のハンドラの 前に 実行する必要があるでしょうから。アップロードハンドラは、リストの前から順に処理されることに注意してください。
If you want to replace the upload handlers completely, you can assign a new list:
request.upload_handlers = [ProgressBarUploadHandler(request)]