Django 汎用クラスビューでファイルをアップロードする方法

2020-04-07 作成: windows10/Python-3.8.2-amd64/Django-3.0.4

Django でファイルを汎用クラスビューでアップロードする方法について、
日本語情報は意外と多くありません。
そのため、結構時間がかかってしまいました。
忘れないように記事にしておきます。
この記事は、ファイルのアップロードの原理を手軽に知りたい、という人向きです。

Django を初めて使う人は、こちら。
[10 分で終わる Django の実用チュートリアル](https://qiita.com/taiyaki/items/352d36cda37af74eb826)

# 前準備

プロジェクト名は mysite、アプリケーション名は fileupload とする。
フォルダ media を mysite/ に作成し、ファイルの保存場所とする。

ここまでのファイルの配置はこうなる。

```
mysite/
    mysite/
        __pycashe__/    <- 気にしなくていい
        setting.py, urls.py など *.py が 5 個
    fileupload/
        migrations/     <- 気にしなくていい
        models.py, views.py など *.py が 6 個
    media/              <- アップロードしたファイルの保存場所
    manage.py           <- プロジェクトの管理用
```


Django プロジェクトに、アプリケーション fileupload を登録する。
そのために、settings.py の INSTALLED_APPS に追記。

```py:mysite/mysite/settings.py
INSTALLED_APPS = [
    'fileupload.apps.FileuploadConfig',
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
]
```

ファイルのアップロード用のディレクトリも登録する。
そのために、settings.py に 2 行を追記。

```py:mysite/mysite/settings.py
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
MEDIA_URL = '/media/'
```

url を登録するため urls.py に追記。

```py:mysite/mysite/urls.py
from django.contrib import admin
from django.urls import include, path
from django.conf import settings
from django.conf.urls.static import static

urlpatterns = [
    path('fileupload/', include('fileupload.urls')),
    path('admin/', admin.site.urls),
] + static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
```

# アプリケーションの実装

モデル Fileupload を作成。
フィールドは、ファイルのタイトルと、ファイルそのものの 2 つ。
ファイルの保存場所は、media/uploads/年/月/日 という名前のフォルダ。
写真をアップロードすることを想定し、拡張子は jpg しか相手にしない。

```py:mysite/fileupload/models.py
from django.db import models
from django.core.validators import FileExtensionValidator

class Fileupload(models.Model):
    titlefield = models.CharField(max_length = 200, null = True)
    filefield = models.FileField(
        upload_to = 'uploads/%Y/%m/%d/',
        verbose_name = 'attached file',
        validators=[FileExtensionValidator(['jpg', ])],
        null = True
    )
```

ビューを作成する。
Django の汎用クラスビューを使うのは、やっぱり楽だから。
汎用クラスビューは、デフォルトの変数名やファイル名など
（たとえば` fileupload_object_name ` や ` template_name ` ）を知っていないと使えない。
だから、デフォルト名ではなく、分かりやすい名前を明示的につけておいた。

```py:mysite/fileupload/views.py
from django.views import generic
from .models import Fileupload

class FileuploadListView(generic.ListView):
    model = Fileupload
    context_object_name = 'fileupload_context_list'

class FileuploadCreateView(generic.CreateView):
    model = Fileupload
    fields = ['titlefield', 'filefield']
    success_url = '/fileupload'
    template_name = 'fileupload/fileupload_create.html'

class FileuploadDetailView(generic.DetailView):
    model = Fileupload
    template_name = 'fileupload/fileupload_detail.html'
    context_object_name = 'fileupload_context'

class FileuploadUpdateView(generic.UpdateView):
    model = Fileupload
    fields = ['titlefield', 'filefield']
    success_url = '/fileupload'
    template_name = 'fileupload/fileupload_update.html'

class FileuploadDeleteView(generic.DeleteView):
    model = Fileupload
    success_url = '/fileupload'
    template_name = 'fileupload/fileupload_delete.html'
```

ビューを url に登録する。
ファイル fileupload/urls.py を新しく作成。
作成したビューを登録する。

```py:mysite/fileupload/urls.py
from django.urls import path
from . import views

urlpatterns = [
    path('', views.FileuploadListView.as_view()),
    path('index', views.FileuploadListView.as_view(),
        name = 'site_index'),
    path('create', views.FileuploadCreateView.as_view(),
        name = 'site_create'),
    path('detail/<int:pk>', views.FileuploadDetailView.as_view(),
        name = 'site_detail'),
    path('update/<int:pk>', views.FileuploadUpdateView.as_view(),
        name = 'site_update'),
    path('delete/<int:pk>', views.FileuploadDeleteView.as_view(),
        name = 'site_delete'),
]
```

` fileupload/templates/fileupload/ `にテンプレート用の html ファイルを作成する。
それぞれのビューに対応する。


```html:mysite/fileupload/templates/fileupload/fileupload_list.html
<h1>ファイルの一覧表示</h1>

{% if fileupload_context_list %}
  <table>
  {% for temp in fileupload_context_list %}
    <tr>
      <td><a href = "{% url 'site_detail' temp.pk %}"> {{ temp.titlefield }}</a></td>
      <td>{{ temp.filefield }}</td>
      <td><a href = "{% url 'site_update' temp.pk %}">Edit</a></td>
      <td><a href = "{% url 'site_delete' temp.pk %}">Delete</a></td>
    </tr>
  {% endfor %}
  </table>

{% else %}
<p>No file available.</p>

{% endif %}
<p><a href = "{% url 'site_create' %}">新しいファイルのアップロード</a></p>
```

```html:mysite/fileupload/templates/fileupload/fileupload_create.html
<h1>ファイルのアップロード</h1>

<form method = "post" enctype="multipart/form-data">
    {% csrf_token %}
    {{ form.titlefield }}
    {{ form.filefield }}
    <input type = "submit" value = "Create" />
</form>
```

```html:mysite/fileupload/templates/fileupload/fileupload_detail.html
<h1>ファイルの詳細</h1>

{% if fileupload_context %}
  <ul>
    <li>Primary Key Number : {{ fileupload_context.pk }}</li>
    <li>Memo Text : {{ fileupload_context.titlefield }}</li>
    <li>Memo Text : /media/{{ fileupload_context.filefield }}</li>
    <li>File : <img src = /media/{{ fileupload_context.filefield }} /></li>
  </ul>

{% else %}
<p>No file available.</p>

{% endif %}
<p><a href = "{% url 'site_index' %}">ファイルの一覧表示</a></p>
```


```html:mysite/fileupload/templates/fileupload/fileupload_update.html
<h1>ファイルの更新</h1>

<form method = "post" enctype="multipart/form-data">
    {% csrf_token %}
    {{ form.titlefield }}
    {{ form.filefield }}
    <input type = "submit" value = "Save" />
</form>
```

```html:mysite/fileupload/templates/fileupload/fileupload_delete.html
<h1>ファイルの削除</h1>

<form method = "post">
    {% csrf_token %}
    <input type= "submit" value = "Delete" />
</form>
```



# 仕上げ

マイグレーションを実行し、開発用 www サーバーを起動すれば、
http://localhost:8000/fileupload/ でアクセスできる。


# 課題
ファイルのアップロードに失敗したときの処理は、ちゃんと書いていないので今後の課題。



おしまい