django でのファイルアップロード

#0.0 主な内容
django(本記事ではversino3.1)でのファイルアップロードに関してまとめます。
この記事を読むことで、
　１　テキストのアップロード
　２　画像のアップロード
　３　オーディオのアップロード
の方法について知ることができます。
主にdjangoやプログラミング初心者を対象とした記事ですが、djangoアーキテクチャの全体像について、基本的な内容は既知のものと想定しています。

また、本記事中では、
　１　post_fileディレクトリ下で下記のコマンドにてプロジェクトを開始し、

```
  $ django-admin startproject post_file . 
```
　２　その後template、static、mediaディレクトリを作成し、以下のようなディレクトリ構成になっている前提とします。

```
$ project/(プロジェクトディレクトリ)
  .
  │── config/（設定ディレクトリ）
  │   │── __init__.py
  │   │── settings.py
  │   │── urls.py
  │   │── wsgi.py
  │── post_file_app/（アプリケーションディレクトリ）
  │   │── __initi__.py  
  │   │── admin.py
  │   │── apps.py 
  │   │── migrations
  │   │   │── __init__.py 
  │   │── models.py 
  │   │── test.py
  │   │── views.py
  │── manage.py
  │── template/ 
  │── static/
  │── media/
  │   │──image/ 
  │   │──audio/ 
```

#1.0 ファイルアップロードまでの主な流れ
ファイルアップロードに関しては、
　１　管理者画面から（サイト管理者が）アップロード
　２　ユーザーフォームから（サイト利用者が）アップロード
の二つが考えられますので、以下、場合分けします。

##1.1 管理者画面から（サイト管理者が）アップロード　
###1.1.1　settings.pyに設定を追記する。

```python:project/config/settings.py
INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'post_file_app',#これを追記
    'media',#これを追記
]

BASE_DIR = Path(__file__).resolve(strict=True).parent.parent#これを追記
MEDIA_ROOT = os.path.join(BASE_DIR,'media')#これを追記
MEDIA_URL = '/media/'#これを追記
```
djangoではアップロードした画像やオーディファイルを直接データベースには格納せずに、事前に用意したファイルに格納し、必要な時はそのパスからファイルを取得します。上記では、MEDIA_ROOTにてその格納するディレクトリを指定しています。

また、MEDIA_URLは、ファイルを表示するときに、/media/<file_name> と言うurlを割り当てて表示するという設定です。

###1.1.2　model.pyにてアップロードするデータのレイアウトを定義する

```python:project/post_file_app/model.py
class PostFile(models.Model):
    usersname = models.CharField(max_length=50)#テキストアップロード
    image =models.ImageField(upload_to='images/',verbose_name='image',null=True,blank=True)#画像アップロード
    audio = models.FileField(default='', upload_to='audio/')#オーディオアップロード
    def __str__(self):
        return self.usersname
```
何をアップロードするかによって、CharField、ImageField、FileFieldを使い分ける点に注意します。
以下二つのコマンドでモデルのマイグレーションも忘れずに行います。

```
$ python manage.py makemigrations
$ python manage.py migrate  
```


###1.1.3 admin.pyに設定を追記し、管理者画面からアップロードできるようにする
model.pyでPostFileクラスを定義しただけでは、管理者画面からアップロードできません。
以下のようにadmin.pyに追記することで、管理者画面からアップロードができるようになります。

```python:project/post_file_app/admin.py

from .models import PostFile
admin.site.register(PostFile)

```


#1.2　ユーザーフォームから（サイト利用者が）アップロード
この場合、単なる管理者画面上からのアップロードに加えて、新たに
　１　入力フォームを作成（htmlファイル作成、form.py作成）
　２　アップロードするファイルのvalidationの実装(views.pyにてロジックを記載)
が必要になります(ただし、本記事内ではvalidationを自作しないので、コードは少しし書く程度です)。

###1.2.1 入力フォームの作成
まずはhtmlファイルを用意します。project/template/ディレクトリに、post.htmlファイルを作成しまし、下記のとおり書きます。　

```html:project/template/post.html 
<!doctype html>
<html lang="en">
    <head>
    </head>
    <body >
    <form method="POST" action='' enctype='multipart/form-data'>{% csrf_token %}
   {{ form.username }}
   {{ form.image }}
   {{ form.audio }}
    </body>
</html>
```
ここでまず、encenctype='multipart/form-data'と言うのは画像やオーディオファイルをアップロードするときにhtmlのformタグ内に記載する必要があります。これを省くと、のちにdjangoは画像、オーディオファイルを取得できません。

また、{% csrf_token %}も呪文のようなもので、ファイルアップロード時に必須となります。

さらに、username、image、audioは、すでに書いたproject/post_file_app/model.pyで記載した変数名（username、image、audio）と一致させる必要があります。

では次にform.pyをpost_file_app内で新たに作成し、以下を追記し入力フォームを作成します。

```python:project/post_file_app/form.py
from django import forms
from .models import PostFile


class PostFileForm(forms.ModelForm):
    def __init__(self,*args,**kwargs):
        super().__init__(*args,**kwargs)
        self.fields['username'].widget.attrs.update({'placeholder':'text'})
        self.fields['image'].widget.attrs.update({'placehodler':'image upload'})
        self.fields['audio'].widget.attrs.update({'placehodler':'audio upload'})
    class Meta:
        model = TestPost
        fields = ('text','image','audio')

```
最後に作成したフォームを表示するために、views.pyでビューを作成し、urls.pyでビューに対してurlを割り当てます。 


```python:project/post_file_app/views.py
from django.shortcuts import render,redirect
from django.views.generic import CreateView
from .forms import PostFileForm

class PostFileView(CreateView):
    def post(self,request,*args,**kwargs):
        form = PostFileForm(request.POST,request.FILES)
        content_dict={
            'form':form
        }
        if not form.is_valid():#ファイルが正しく入力されているかチェック（validation）
            return render(request,'error.html')#error.htmlは、記事中では作成してませんが、各自用意してください。
        form.save()
        return redirect('success_page')
    def get(self,request,*args,**kwargs):
        form = PostFileForm(data=request.GET)
        return render(request,'post.html',{'form':form,})

```
ここで、formと言う変数が出ていますが、これは既述したpost.htmlのform.username、form.image、form.audioのformと一致させてください。
また、PostFileForm(　)の引数として、request.POSTとrequest.FILESとありますが、
前者はdjangoがテキストを、後者は画像やオーディオファイルを取得するために必要なものです。

```python:project/post_file_app/urls.py
from django.urls import path
from .views import Register,LoginView,LogoutView
from mystudio.views import RenderPostedSong
from .views import PostSongView,TestPostView,TestPostView,RenderTestPost

urlpatterns = [
    path('post_file_view/',PostFileView.as_view(),name='post_file_view'),
    path('success/',success,name='success_page',#本記事内ではviewを定義していません
    path('error/',error,name='error_page',#本記事内ではviewを定義していません
]
```

#終わり
以上です。これでdjangoにてテキスト、画像、オーディオファイルのアップロードができるようになりました。