クラスベースViewを用いて、Djangoでファイルをアップロードする方法をご紹介します。
以前の記事「Djangoでファイルをアップロードする方法」を基に実装を変更します。
目次
条件
- Django 2.1.4
- Python 3.7.0
アップロード処理の実装(クラスベースView)
アップロード先ディレクトリ
アップロードしたファイルの保存先は変更なしです。
projectName ├projectName └monitor ├migrations ├templates └uploads ←ここにアップロードしたファイルが保存される ・・ ・・
urls.py
pathの設定でviewsのクラスベースViewクラスを参照するよう変更します。
コメントアウトしている箇所は以前の実装(関数ベースView)です。
# urls.py抜粋
from django.urls import path
from . import views
app_name = 'monitor'
urlpatterns = [
・・・
# ファイルアップロード用
# path('monitor/upload/', views.upload, name='upload'),
# path('monitor/upload_complete/', views.upload_complete, name='upload_complete'),
path('monitor/upload/', views.Upload.as_view(), name='upload'),
path('monitor/upload_complete/', views.UploadComplete.as_view(), name='upload_complete'),
]
views.py
今回は「FormView」を用いて実装します。
コメントアウトしている箇所は以前の実装(関数ベースView)です。
# views.py抜粋
import os
from .forms import UploadFileForm
from django.views.generic.edit import FormView
UPLOAD_DIR = os.path.dirname(os.path.abspath(__file__)) + '/uploads/' # アップロードしたファイルを保存するディレクトリ
# アップロードされたファイルのハンドル
def handle_uploaded_file(f):
path = os.path.join(UPLOAD_DIR, f.name)
with open(path, 'wb+') as destination:
for chunk in f.chunks():
destination.write(chunk)
# # ファイルアップロード
# def upload(request):
# if request.method == 'POST':
# form = UploadFileForm(request.POST, request.FILES)
# if form.is_valid():
# handle_uploaded_file(request.FILES['file'])
# return redirect('monitor:upload_complete') # アップロード完了画面にリダイレクト
# else:
# form = UploadFileForm()
# return render(request, 'monitor/upload.html', {'form': form})
# # ファイルアップロード完了
# def upload_complete(request):
# return render(request, 'monitor/upload_complete.html')
# ファイルアップロード
class Upload(FormView):
template_name = 'monitor/upload.html'
form_class = UploadFileForm
def get_context_data(self, **kwargs):
context = super().get_context_data(**kwargs)
form = self.get_form()
context = {
'form': form,
}
return context
def form_valid(self, form):
handle_uploaded_file(self.request.FILES['file'])
return redirect('monitor:upload_complete') # アップロード完了画面にリダイレクト
# ファイルアップロード完了
class UploadComplete(FormView):
template_name = 'monitor/upload_complete.html'
form_class = UploadFileForm
forms.py
実装方法の変更はありませんが、拡張子のバリデーションチェックを追加しています。
# forms.py
from django import forms
import os
VALID_EXTENSIONS = ['.csv']
class UploadFileForm(forms.Form):
file = forms.FileField(
label='アップロードファイル',
)
def clean_file(self):
file = self.cleaned_data['file']
extension = os.path.splitext(file.name)[1] # 拡張子を取得
if not extension.lower() in VALID_EXTENSIONS:
raise forms.ValidationError('csvファイルを選択してください!')
テンプレート
アップロード画面についてエラーメッセージ出力を明示的に実装しています。
アップロード完了画面は変更無しです。
<!-- upload.html -->
{% extends 'base.html' %}
{% block content %}
<h1>アップロード サンプル</h1>
<form method="POST" enctype="multipart/form-data">
{% csrf_token %}
{% if form.errors %}
{% for field in form %}
{% for error in field.errors %}
<div class="alert alert-danger">
<strong>{{ error|escape }}</strong>
</div>
{% endfor %}
{% endfor %}
{% for error in form.non_field_errors %}
<div class="alert alert-danger">
<strong>{{ error|escape }}</strong>
</div>
{% endfor %}
{% endif %}
{% for field in form %}
<div class="form-group form-inline">
<div class="col-md-8">
{{ field }}
</div>
</div>
{% endfor %}
<div class="form-group">
<button type="submit">アップロード</button>
</div>
</form>
{% endblock %}
<!-- upload_complete.html -->
{% extends 'base.html' %}
{% block content %}
<h1>アップロード完了</h1>
<a href="{% url 'monitor:upload' %}"><button>戻る</button></a>
{% endblock %}
実行結果
Djangoを起動して以下のURLにアクセスします。
http://127.0.0.1:8000/monitor/upload/
ファイル選択とアップロードボタンが表示されます。
ファイルを選択せずにアップロードボタンを押すと、警告メッセージが表示されます。
csvファイル以外のアップロードを試みるとエラーメッセージが出力されます。
アップロードに成功すると、アップロード完了画面に遷移します。
指定のディレクトリに対象ファイルがアップロードされます。
以上で、クラスベースViewを用いたアップロード処理実装の紹介は終わりです。
参考
Django公式:クラスベースのビューでフォームを扱う
https://docs.djangoproject.com/ja/2.1/topics/class-based-views/generic-editing/
stackoverflow:formのエラーメッセージ出力
https://stackoverflow.com/questions/14647723/django-forms-if-not-valid-show-form-with-error-message

