Djangoで画像アップロードフォームを作成し、サーバー上に保存してみようと思います。
環境
Windows10 64bit
Anaconda3
Python 3.7.3
Django2.2
①事前準備
Anacondaで環境作成
conda create -n myapp conda activate myapp
Djangoのインストール
conda install Django
画像処理ライブラリPillowのインストール
pip install pillow
プロジェクトの作成
django-admin startproject myproject cd myproject
アプリの作成
python manage.py startapp myapp
日本語化
myproject/myproject/settings.pyを以下のように変更
LANGUAGE_CODE = 'ja' TIME_ZONE = 'Asia/Tokyo'
プロジェクトに「myapp」を認識させる
INSTALLED_APPS = [ "myapp", 'django.contrib.admin', 'django.contrib.auth', 'django.contrib.contenttypes', 'django.contrib.sessions', 'django.contrib.messages', 'django.contrib.staticfiles', ]
テンプレートフォルダの作成とパス指定
templates\myappを作成し、myproject/myproject/settings.pyにテンプレートパスを指定
TEMPLATES = [ { 'BACKEND': 'django.template.backends.django.DjangoTemplates', 'DIRS': [os.path.join(BASE_DIR, 'templates'),], 'APP_DIRS': True, 'OPTIONS': { 'context_processors': [ 'django.template.context_processors.debug', 'django.template.context_processors.request', 'django.contrib.auth.context_processors.auth', 'django.contrib.messages.context_processors.messages', ], }, }, ]
初回migrate
>python manage.py migrate
サーバーテスト起動
>python manage.py runserver
②MEDIA_URLの作成
画像がアップロードされた際のパスを指定しておきます。
myproject/myproject/settings.pyに以下を追記します。
MEDIA_URL = '/media/' MEDIA_ROOT = os.path.join(BASE_DIR,"media")
これにより、アップロードされた画像はmyproject/media/mediaに保存されるよう設定することができました。
※mediaディレクトリはアップロード処理時に自動で作成されます。
③モデルの定義
myproject/myapp/models.pyを編集していきます。
models.pyはDjangoの機能の中で、主にデータベースへのアクセスを請け負っている部分になります。
今回の場合は画像処理なので「ImageField」という関数を使用し、先ほど設定したmediaディレクトリに保存する機能を作成していきます。
ImageFieldを使用することで、特定のディレクトリ(今回はmedia)に画像ファイルを保存し、ファイルパスをDBに保存することができます。
Djangoの場合、models.pyで作成したclassがデータベースのテーブルに該当しており、今回は「Photo」というテーブルに「image」カラムを作成します。
またアップロードした画像へのパスを取得するreturn_photo関数も作成しておきます。
from django.db import models class Photo(models.Model): image = models.ImageField(upload_to='media') def return_photo(self): return self.image.url
upload_to=はMEDIA_ROOT(今回はmyproject/media)を自動で読み込みます。
upload_to='media'とすることでmyproject/media/mediaへ保存されるよう設定ができました。
models.pyを編集したので、コマンドラインで以下を実行しておきます。
python manage.py migrate
④forms.pyの作成
myproject/,myapp/forms.pyを作成し,、画像アップロードフォームを実装していきます。
今回はPhotoFormというクラスを作成していきますが、Djangoのformsクラスを継承することで簡単に作成することができます。
from django import forms class PhotoForm(forms.Form): image = forms.ImageField()
⑤テンプレートファイルの作成
今回はメインとなるindex.htmlとアップロード後に表示されるresult.htmlの2種類を作成します。
index.html
<table> <form action="{% url 'index' %}" method="POST" enctype="multipart/form-data"> {% csrf_token %} {{ form.as_p }} <input type="submit" value="submit" /> </form> </table>
DjangoではPythonで扱っていた変数をhtmlに流用して使うことが可能です。
重要なところを順番に説明していきます。
form action="{% url 'index' %}" method="POST" enctype="multipart/form-data"
ここではformでアクションが行われた場合の操作を記載しています。
action="{% url 'index' %}" でmyapp/urls.pyの name="index"のパターンを、
method="POST"でPOSTリクエストとして呼び出す、という記載をしています。
今回の場合には「送信」ボタンが押された際に上記のリクエストを送るような処理になります。
{{ form.as_p }}
forms.pyで作成した画像アップロードフォームをレンダリングします。
とはいっても{{ form.as_p }}と記載すればフォームが表示されるわけではなく、views.pyで「forrm」という変数にforms.pyで作成した PhotoForm()を格納する処理が必要になります。
views.pyで定義をした後に、formの変数を{{form}}とテンプレートファイルに記載することでフォームを実装することができます。
ちなみに.as_pは段落として表示する場合に記載するものでhtmlの
タグと同じです。⑥urlの設定
まずはプロジェクトレベルのURLを設定します。
myproject/,myproject/urls.py
from django.conf.urls import include, url from django.contrib import admin urlpatterns = [ url(r'^myapp', include('myapp.urls')), url(r'^admin/', admin.site.urls), ]
次にmyproject/myapp/urls.pyを作成し、以下を記載。
from django.conf.urls import url from . import views urlpatterns = [ url(r'^/', views.index, name='index'), ]
これでhttp://127.0.0.1:8000/myapp/へアクセス時にmyproject/,myapp/views.pyのindex関数が呼び出されるようになりました。
⑦vies.pyの設定
リクエストがあった場合の処理を記載していきますが、この部分がかなり重要になります。
from django.shortcuts import render, redirect from .forms import PhotoForm from .models import Photo from django.http import HttpResponse from .import views def index(request): if request.method == 'GET': return render(request, 'myapp/index.html', { 'form': PhotoForm(), }) elif request.method == 'POST': form = PhotoForm(request.POST, request.FILES) if not form.is_valid(): raise ValueError('invalid form') photo = Photo() photo.image = form.cleaned_data['image'] photo.save() input_photo = photo.return_photo() return render(request,'myapp/result.html')
順番に解説していきます。
最初にindex関数を作成します。今回はフォーム機能の実装なので、if文でGET・POSTそれぞれのリクエストがあった場合の処理を記載していきます。
まずGETの場合ですがこの場合は普通にindex.htmlをレンダリングしていきます。
ですがindex.htmlの中にアップロードフォームを表示する必要があります。
なので
return render(request, 'myapp/index.html', { 'form': PhotoForm(), })
の部分で、index.htmlの{{ form }}と記載のある部分にforms.pyで定義したPhotoForm(),を表示するようにします。
このようにDjangoではrender関数の引数として特定の変数を指定し、html上で{{ }}で囲って使用することでPython上の変数をhtmlで引き継ぐことができます。
次にPOST時の処理を記載しています。
form = PhotoForm(request.POST, request.FILES) if not form.is_valid(): raise ValueError('invalid form')
の部分でフォームに投稿されたファイルの形式をチェックしています。
今回は画像アップロードフォームなので、ファイルが画像じゃない場合にエラーを吐き出すようにしています。
photo = Photo()
photo.image = form.cleaned_data['image']
photo.save()
input_photo = photo.return_photo()
次にphoto = Photo()でmodels.pyのPhotoクラスのインスタンスを作成し、Photoクラスのimage関数ででアップロードされた画像の情報を取得しています。
またphoto.save()で取得した画像ファイルパスをデータベースに追加しています。
(input_photo = photo.return_photo()の部分は取得した画像のパス情報を格納している部分です。画像処理につなげる場合なんかに使えます。)
このようにforms.pyとmodels.pyを読み込んで様々な処理を行うことができました。
最後に
return render(request,'myapp/result.html')
でresult.htmlをレンダリングして完成です。