クラスベース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