Pillowのインストール
後程解説するModelの箇所で紹介しますが、ImageFieldを利用するためには、Pillowのインストールが必須の様子です。
settings.pyにMEDIA_ROOTの設定
アップロードされる画像の保存先をMEDIA_ROOTとしてsettings.pyに定義します。
通常は、プロジェクトフォルダ直下に”media”という名称で作成します。
# pj/settings.py
import os
MEDIA_ROOT = os.path.join(BASE_DIR, 'media/')
BASE_DIRはデフォルトの場合、プロジェクトフォルダになりますので、プロジェクトフォルダ直下に’media’というフォルダが作成され、ここにアップロードされた画像が保存されるようになります。
尚、このmediaフォルダは手動で作成する必要はなく、上記の設定をしていれば初回画像アップロード時に自動的にmediaフォルダが作成されます。
画像アップロード用のModel作成
ImageFieldを持ったクラスをModelに定義すると、そのクラスから生成されたインスタンスは①データベースへ保存される 、②MEDIA_ROOTに画像が保存される というように2か所にデータが保存されることになります。通常のModelで定義されるFieldであれば①のデータベースへの保存のみですが、ImageFieldは②の役割も担ってくれるという面で特殊なフィールドです。
models.py
# pj/app/models.py
from django.db import models
class UploadImage(models.Model):
image = models.ImageField(upload_to = 'images/')
また、上記のようにImageFieldのキーワード引数”upload_to”にフォルダ名を指定することで、MEDIA_ROOT以下の保存先を指定することが出来ます。
上記の例では、”pj/media/ MEDIA_ROOT images/ upload_to ” が保存先に指定されていることになります。
画像アップロード用のFormの作成
以前の解説記事で、モデルを継承してModelFormというフォームモデルを作成すれば、フォームから入力したデータがデータベースに保存されることを解説しました。
この時にフォームモデルに、ImageFieldを持たせれば、画像アップロード用のフォームが作成されます。
forms.py
from django import forms
from .models import *
class UploadForm(forms.ModelForm):
class Meta:
model = UploadImage
fields = ['image']
def __init__(self, *args, **kwargs):
for field in self.base_fields.values():
field.widget.attrs["class"]="form-control"
super().__init__(*args, **kwargs)
※init 関数の部分は、Bootstrapの「form-control」クラスを各フィールドに適用するために実装しています。必須ではありませんので、必要に応じて実装を検討下さい。
上記をビュークラスからテンプレートに渡して、ブラウザ上に表示させると下記のような画面が表示されます。
そのため、次の作業は、このテンプレートの作成とforms.pyをテンプレートに引き渡す仕組みをビュー関数に作成することになります。
views.py
views.pyには2つの役割が実装されています。
GETリクエスト が来た際に、フォームをテンプレートに渡す。 (params={}とreturn render()の部分がこの処理に相当します。)
POSTリクエスト が来た際に、下記の処理を実行する。① フォームに、Request のPOST情報 とFILES情報 を引数に渡してフォームインスタンスを作成 ② ①で作成したフォームインスタンス に対して”is_valid()”メソッドを使ってバリデーション を行います。③ ②の結果、正常なデータが取得されている場合、インスタンスの親クラス(ModelForm)のsave()メソッド を使ってフォームインスタンスをデータベースに保存 します。④ テンプレートに引き渡すパラメータ(param)にidを追加し、③でデータベースに保存したフォームインスタンスのidを格納 します。
views.py
from django.shortcuts import render
from .forms import UploadForm
from .models import UploadImage
def index(request):
params = {
'title':'画像のアップロード',
'upload_form':UploadForm(),
'id':None,
}
if (request.method == 'POST'):
form = UploadForm(request.POST, request.FILES) # ①
if form.is_valid(): # ②
upload_image = form.save() # ③
params['id'] = upload_image.id # ④
return render(request, 'app/index.html', params)
テンプレート作成
テンプレート作成時の注意点は下記の2点です。
views.pyから引き渡されるforms.pyで定義したフォームクラス をテンプレートに{{ フォームクラス名.as_p }} のようにして引き渡す。
formタグの属性に「 method=”post” enctype=”multipart/form-data” 」を設定する。
formタグのaction属性の属性値 は、URLテンプレートタグ を使って、app/urls.pyに設定されているname=”テンプレート名” を設定する。*
*URLテンプレートタグは、urls.pyでnameを付けられているpath関数に紐づけることが出来ます。
pj/templates/app/index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link rel="stylesheet" href="{% static 'style.css' %}">
<title>{{ title }}</title>
</head>
<body>
<h1>{{title}}</h1>
<div>
{% if id is not None %}
<h2>画像が登録されました</h2>
<p>画像のIDは {{id}} です</p>
{% endif %}
</div>
<div>
<h1>{{ title }}</h1>
<h2>画像を登録する。</h2>
<form action="{% url 'app/index' %}" method="post" enctype="multipart/form-data">
{% csrf_token %}
<p>{{ upload_form.as_p }}</p>
<p><input type="submit" value="アップロード"></p>
</form>
</div>
</body>
</html>
urls.py
トップページにアクセス (下記のpath()関数内のURL位置引数=’ ‘の箇所がトップページへのアクセス時の処理を示します。)があった際、appのurls.pyにリクエストを転送 するように設定しています。
pj/urls.py
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('', include('app.urls')),
path('admin/', admin.site.urls),
]
トップページにアクセス があった際、views.pyに設定されているindex関数 とテンプレート名=’app/index’ を紐づける ように設定しています。
app/urls.py
from django.contrib import admin
from django.urls import path
from . import views
urlpatterns = [
path('', views.index, name='app/index'),
path('admin/', admin.site.urls),
]
以上で画像を保存することが出来ます。
画像のアップロード後に下記のように表示されていれば成功です。
アップロードファイルを表示する方法
アップロードされた画像を表示する方法は、基本的には以前解説した静的ファイルの扱い方の方法を踏襲すれば大丈夫です。下記の記事でも解説している通り、静的ファイルがブラウザに表示される手順は下記のとおりです。
①ブラウザがHTMLファイルを読み込む
②ブラウザがHTMLファイル内の<link>や<img>などの静的ファイルのタグを発見すると、そのhref属性値やsrc属性値に設定されているURLをウェブサーバーにリクエストします。
③URLがSTATIC_URL で始まっているファイルのリクエストの場合、ウェブサーバー(Django開発環境の場合、Django開発環境用ウェブサーバーソフト)が静的ファイルの要求だと認識して、STATIC_ROOT(静的ファイル保存場所)の中を検索し、ファイルを取得してブラウザに返す 。
アップロードされたファイルについても上記と同様に、開発用ウェブサーバーソフトが静的ファイルであることを認識してもらうように設定する必要 があります。
静的ファイル の場合は、ファイルのURLがSTATIC_URL から始まる場合、静的ファイルであると認識されました。
アップロードファイル の場合は、ファイルのURLがMEDIA_URL から始まる場合に、アップロードファイルのリクエストであると判断されます。
MEDIA_URLは、settings.pyに手動で追加する必要があります。
settings.py
MEDIA_URLの定義
pj/urls.py
次に、MEDIA_URL とMEDIA_ROOT の紐づけ を行う必要があります。 MEDIA_URLから始まるファイルの要求があった場合、開発用ウェブサーバーソフトがMEDIA_ROOTからファイルを検索してもらうための紐づけが必要です。
そのためには、プロジェクトのurls.pyに下記のコードを入力します。
pj/urls.py
from django.contrib import admin
from django.urls import path, include
from django.conf import settings
from django.conf.urls.static import static
urlpatterns = [
path('', include('app.urls')),
path('admin/', admin.site.urls),
]
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
テンプレート
テンプレートでは、シンプルにimgタグのsrc属性値にviews.pyからURLを受け取れるようにするだけです。
templates/display.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>{{ title }}</title>
</head>
<body>
<h1>{{ title }}</h1>
<div>
<h2>ID:{{ id }}の画像</h2>
<img src="{{ url }}">
</div>
</body>
</html>
views.py
get_object_or_404(モデルクラス, pk=id) を用いてモデルクラス内でidに合致するモデル があるかないかを検索しています。尚、idには、後のアプリケーションのurls.pyに登場しますが、リクエストURLの末尾にidを追加することになります。このidを用いて該当するモデルを検索します。
views.py
from django.shortcuts import render, get_object_or_404
from django.views.generic import TemplateView
from .forms import UploadForm
from .models import UploadImage
# Create your views here.
class Index(TemplateView):
template_name = 'app/index'
def index(request):
params = {
'title':'画像のアップロード',
'upload_form':UploadForm(),
'id':None,
}
if (request.method == 'POST'):
form = UploadForm(request.POST, request.FILES)
if form.is_valid():
upload_image = form.save()
params['id'] = upload_image.id
return render(request, 'app/index.html', params)
def display(request, image_id=0):
upload_image = get_object_or_404(UploadImage, id=image_id)
params = {
'title':'画像の表示',
'id':upload_image.id,
'url':upload_image.image.url,
}
return render(request, 'app/display.html', params)
app/urls.py
app/urls.py
from django.contrib import admin
from django.urls import path
from . import views
urlpatterns = [
path('', views.index, name='app/index'),
path('display/<int:image_id>', views.display, name='display'),
]
上記の設定を行った後、通常通りpython manage.py runserverを行います。
今回は、ID=2として画像のアップロードを実行しました。
そして、ブラウザのURL入力欄に下記のように、テンプレート名/ID を入力してリクエストを送信すると下記のようにアップロードした画像が返されます。
コメント