サイトアイコン 知的好奇心

Djangoでファイルをアップロードする方法(クラスベースView)

クラスベースViewを用いて、Djangoでファイルをアップロードする方法をご紹介します。

以前の記事「Djangoでファイルをアップロードする方法」を基に実装を変更します。

条件

アップロード処理の実装(クラスベース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

モバイルバージョンを終了