このページでは、Django での「画像アップロード」を行うための手順について解説していきます。
単に手順だけを説明するのではなく、実際に画像アップロード可能なウェブアプリを開発しながら、画像アップロードを行う手順を解説していきたいと思います。
画像のアップロードの仕方を理解しておくことで開発できるウェブアプリの幅が広がります。実際 Twitter や Instagram 等でも画像のアップロードができますよね!
今回紹介するアプリは簡単なものですが、Django での画像のアップロードにも慣れることができると思いますし、今後のウェブアプリ開発に役に立つ点もあると思いますので、是非手を動かしながら読み進めてみてください!
また、アップロードされた画像ではなく、静的ファイルとして用意した画像の扱いについては下記ページで解説しています。アップロードされた画像であっても、画像の表示の仕方などは静的ファイルの時と同様の点も多いですので、こちらも是非読んでみてください。
【Django】静的ファイルの扱い方(画像・CSS・JS)開発するウェブアプリ
前述の通り、このページでは画像アップロードの手順をウェブアプリを開発しながら解説していきたいと思います。まずは、この開発するウェブアプリについて紹介しておきます。
今回開発するウェブアプリにおいて出来ることは下記の2つのみになります。
- 画像のアップロード
- アップロードされた画像の表示
まず、画像のアップロードは、下記の URL から行います。
http://localhost:8000/upload_app/
以降は上記の URL がアプリのトップページの URL であることを前提に解説を進めていきますので、ポート番号やアプリ名を変更する場合は、適宜その部分を読み替えながら解説を読み進めていただければと思います。
上記の URL にアクセスすれば、下の図のようなページが表示されます。
ここで ファイルを選択
ボタンをクリックすれば、ファイル選択ダイアログが表示されてファイルを選択することができ、ファイル選択後に アップロード
ボタンをクリックすれば選択したファイルがアップロードされます。
アップロードが完了すると下の図のようなページに遷移します。下側のフォームから続けて画像のアップロードを行うことも可能です。
アップロードされた画像を表示する際には、上の図のようにアップロード完了した際に表示される画像の ID
を使用して URL を指定します。
具体的には、下記のように URL に ID
を指定すれば、その ID
を持つ画像を表示することができます。
http://localhost:8000/upload_app/preview/ID
表示結果は下の図のようになります。
出来ることはこれだけです…。ただ、これだけ実現するだけでも、Django での画像のアップロードについて十分学ぶことができると思います!
今回は、まず画像のアップロードが可能なウェブアプリを開発し、その後にアプリを変更してアップロードした画像を表示できるようにしていきたいと思います。
画像のアップロードを実現に必要なこと
続いて、Django で開発するウェブアプリで画像のアップロードを実現するために必要なことを整理しておきます。
まず、ユーザーが画像のアップロードを行えるよう、画像のアップロードが可能なフォームを作成する必要があります。より具体的には、アップロードする画像の選択を行う ファイルを選択
ボタンを持つようなフォームを作成する必要があります。
このフォームをページに表示することで、ユーザーが画像をアップロードできるようになります。
また、アップロードされた画像をウェブアプリで使用する(表示したり加工したり)ことを考えると、画像をファイルとして保存する処理も必要になります。
さらに、保存された画像の情報をウェブアプリで管理していく必要があります。必須なのが、画像ファイルの保存先のパスの情報の管理です。
アップロードされた画像を後から表示したり加工したりする際には、そのアップロードされた画像ファイルを後から取得できるようにしておく必要があります。その際に、アップロード画像の保存先のパスが必要になります。
また、アップロードされた画像に対し、画像の名前やアップロードされた日時、画像へのコメント等を付加情報として一緒に管理するのであれば、そういった情報も管理する必要があります。
こういった情報の管理は、通常のウェブアプリ同様に、データベースで行います。
で、データベースを扱うのは Django では Model の役割になります。なので、保存された画像の情報をウェブアプリで管理するために models.py
を変更して Model を作り込んでいく必要があります。
アップロードされた画像を扱う際に重要なのが、この Model に ImageField
というフィールドを持たせておくことです。
この ImageField
というフィールドを持たせておくことで、その Model から簡単に画像アップロード用のフォームを作成することができます。
また、この ImageField
というフィールドを持たせておけば、アップロードされた画像のファイル保存、さらにはアップロードされた画像の保存先のパスの管理も簡単に行うことができます。
つまり、ImageField
を持たせた Model を作成しておけば、前述の画像アップロードを実現する上で必要な下記の3つを簡単に行うことができます。
- 画像アップロード用のフォームの作成
- アップロードされた画像の保存
- 保存された画像の情報の管理
ImageField
がどのように作用するのかについては、画像のアップロード で実際に画像のアップロードが可能なウェブアプリを開発しながら解説していきたいと思いますが、まずは Model に ImageField
を持たせることが重要であることを覚えておいてください。
また、アップロードされた画像の使用例の1つとして、アップロードされた画像の表示の仕方についても解説していきたいと思います。アップロードされた画像の表示に関しては、基本的には下記ページで解説している静的ファイルを扱う際の手順とほとんど同じです。
【Django】静的ファイルの扱い方(画像・CSS・JS)ただ、アップロードファイルへのリクエストの判断方法やファイルの取得先となるフォルダ等が静的ファイルの時と異なります。この辺りを踏まえながら、アップロードされた画像を表示する際の手順についても後述の アップロードされた画像の表示 で紹介していきたいと思います。
スポンサーリンク
画像のアップロード
大雑把ですが、画像のアップロードを実現する上で必要なことは解説しましたので、次は実際にウェブアプリを開発していきながら Django での画像アップロードを実現する具体的な手順を解説していきたいと思います。
前準備:PIL のインストール
まず、前準備として PIL が未インストールの方は PIL をインストールしておいて下さい。
PIL は下記の pip
コマンドでインストール可能です。
python -m pip install Pillow
先ほど画像のアップロードを実現する際には ImageField
が重要であると述べましたが、この ImageField
は画像処理モジュールである PIL を利用するようです。
そのため、ImageField
を利用するためには事前に PIL をインストールしておく必要があります。
ちなみに、PIL をインストールせずに ImageField
を利用しようとすると、下記のようなエラーが発生することになります。
(fields.E210) Cannot use ImageField because Pillow is not installed.
PIL を利用することで Python でお手軽に画像を扱うことができるようになりますので、インストールしておいて損はないと思います。
プロジェクトとアプリの作成
PIL のインストールが完了したら、次は実際にアプリを開発していきましょう!
まずは、いつも通りプロジェクトとアプリを作成していきます。
プロジェクトとアプリの作成
今回はプロジェクト名を upload_project
、アプリ名を upload_app
として作成していきたいと思います。
まずは下記コマンドを実行してプロジェクト upload_project
を作成します(コマンドの %
部分は入力不要です。以降も同様です)。
% django-admin startproject upload_project
以降の解説で紹介するパスは全て、このコマンドで作成された upload_project
以降のパスのみで記述させていただきます。
続いて下記コマンドで upload_project
フォルダの中に移動し、
% cd upload_project
さらに、下記コマンドでアプリ upload_app
を作成します。
% python manage.py startapp upload_app
アプリの登録
次は、プロジェクトへのアプリ(upload_app
)の登録を行います。
これに関しても、いつも通り upload_project/upload_project/settings.py
の INSTALLED_APPS
にアプリ名を追加することで実現できます。
INSTALLED_APPS = [
'upload_app', # 追加
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
]
スポンサーリンク
画像のアップロード先フォルダの設定
続いて、画像アップロード先のフォルダの設定を行なっていきます。
アップロード先フォルダの設定
アップロード先のフォルダは MEDIA_ROOT
の定義により設定できます。
具体的には、upload_project/upload_project/settings.py
に下記のように MEDIA_ROOT
を定義します。これにより、アップロードされたファイルが MEDIA_ROOT
以下に保存されるようになります(フォルダのパス
のフォルダ以下に保存される)。
MEDIA_ROOT = フォルダのパス
今回は、upload_project/media/
をアップロード先のフォルダとして設定したいと思います。そのために、下記のように upload_project/upload_project/settings.py
に追記を行います。
追記する場所は upload_project/upload_project/settings.py
の最後付近にある STATIC_URL = 略
の下あたりで良いと思います。
import os
MEDIA_ROOT = os.path.join(BASE_DIR, 'media/')
BASE_DIR
はデフォルトだとプロジェクトフォルダとなります。ですので、プロジェクトとアプリの作成 の startproject
実行時に作成された upload_project
フォルダ直下の media
フォルダが MEDIA_ROOT
に設定されることになります。つまり manage.py
や upload_app
フォルダと同階層の media
フォルダということですね!
この MEDIA_ROOT
のフォルダは画像アップロード時に自動的に作成されるので、フォルダを手動で作成する必要はありません。
画像アップロード用の Model の作成
ここからは、アプリ側のファイルの変更を行なっていきます(upload_project/upload_app
以下のファイル)。
まずは Model を作成していきます。
前述の通り、Model を作成する上でポイントになるのが、ImageField
を持たせておくことになります。
ということで、ImageField
を持つ Model を作成していきます。具体的には、下記のように upload_project/upload_app/models.py
を変更します。これにより、ImageField
を持った Model の UploadImage
が定義されることになります。
from django.db import models
class UploadImage(models.Model):
image = models.ImageField(upload_to='img/')
この ImageField
を持った Model には下記のような特徴があります。
まず前提として、マイグレーションを行うと、Model の持つフィールドに合わせたテーブルがデータベースに作成されます。そして、Model のインスタンスに save
メソッドを実行させれば、作成されたテーブルに合わせたレコードがデータベースに保存されます(プライマリーキーを指定していない場合、id
が自動的に割り振られることになります)。
ImageField
を持つ Model の場合は少し特殊で、インスタンスにアップロードされた画像をセットした状態で save
メソッドを実行させた場合、データベースへのレコードの保存だけでなく、アップロードされた画像のファイル保存も行われます。そして、ファイルの保存先のフォルダは MEDIA_ROOT
以下となります。
また、上記のように ImageField
に upload_to
を指定することで、アップロードされた画像を MEDIA_ROOT
以下のどのフォルダに保存するかを指定することができます。上記のように upload_to='img/'
を指定すれば、MEDIA_ROOT
のフォルダにある img
フォルダの中にアップロードされた画像がファイル保存されることになります。
さらに、画像ファイルが保存されるのと同時に、そのファイルの保存先のパスをフィールドとして持つレコードがデータベースに保存されます。より具体的には、保存された画像ファイルの “MEDIA_ROOT
からの相対パス” をフィールドとして持つレコードがデータベースに保存されます。
今回用意した Model は ImageField
以外のフィールドを持ちませんが、もちろん ImageField
以外のフィールド、例えばアップロードされた日時や画像の名前用のフィールドを持たせている場合は、それらの情報も1つのレコードにまとめてデータベースに保存されることになります。
また、データベースからレコードを取得して Model のインスタンスを生成することで、これらの情報を後から取得することも可能です。
つまり、ImageField
を持った Model のインスタンスにアップロードされた画像をセットした状態で save
メソッドを実行させることで、下記の2つを実現することができます。
- アップロードされた画像の保存
- 保存された画像の情報の管理
また、Form に関しては、ModelForm
を継承して Form を作成することで、Model に基づいた Form を作成することが可能です。この時に、ImageField
を持った Model に基づいた Form を作成すれば、画像アップロード用のフォームが作成されることになります。
つまり、これによって下記も実現することができます。
- 画像アップロード用のフォームの作成
ということで、画像のアップロードを実現に必要なこと で挙げた3つが ImageField
を持った Model を作成することで簡単に実現できます。
といっても、上記では手順としては上辺しか解説していませんので、次は実際に ImageField
を持った Model を利用しながら上記を実現していくための具体的な手順を解説していきます。
画像アップロード用の Form の作成
まずは Form について解説していきます。
ユーザーが画像のアップロードができるよう、画像アップロード用の Form を作成していきます。
で、これは前述のように、ModelForm
を継承して Form を作成することで簡単に実現できます。
今回は、upload_project/upload_app/forms.py
を下記のように新規作成することで、画像ファイルアップロード用のフォームを作成していきます(upload_project/upload_app/forms.py
は手動で新規作成する必要があります)。
from django import forms
from .models import UploadImage
class UploadForm(forms.ModelForm):
class Meta:
model = UploadImage
fields = ['image']
上記のように ModelForm
を継承する形で Form を定義すれば、Meta
の model
で指定した Model 用の Form を作成することができます。さらに、その Form で扱う入力フォームは fields
で指定したもののみとなります。
上記では model = UploadImage
と fields = ['image']
を指定しているので、UploadForm
は UploadImage
の image
用の入力フォームを扱う Form ということになります。さらに、image
の実体は ImageField
であり、この入力フォームは画像アップロード用の ファイルを選択
ボタンとなります。
つまり、上記の UploadForm
のインスタンスを生成すれば、画像アップロード用の ファイルを選択
ボタンを持つフォームを作成することができます。
あとは、このインスタンスを Template に渡して render
関数でレンダリングすれば、画像アップロード用の ファイルを選択
ボタンを持つフォームのタグを HTML 内に埋め込むことができます。したがって、ページに画像アップロード用のフォームを表示させることができ、ユーザーからの画像のアップロードを受け付けることができます。
スポンサーリンク
画像アップロード用の Template の作成
続いては、画像アップロード用の Template を作成します。
この Template 作成時には2点ポイントがあります。
1点目は、先ほど作成した UploadForm
のインスタンスを受け取り、それが <form>
〜 </form>
の中に埋め込まれるように Template を作成する点になります。
2点目は、画像アップロード用のフォームに対応する <form>
タグには、属性 enctype="multipart/form-data"
を指定する必要があるという点になります。
あとは大体普通のフォーム表示時と同様に作成すれば良いです。
今回は、下記のようなテンプレートを upload_project/upload_app/templates/upload_app/index.html
に作成したいと思います(新規作成が必要なファイルになります)。
<!doctype html>
<html lang="ja">
<head>
<meta charset="utf-8">
<title>{{title}}</title>
</head>
<body>
<h1>{{title}}</h1>
<div>
{% if id is not None %}
<h2>画像が登録されました</h2>
<p>画像のIDは {{id}} です</p>
{% endif %}
</div>
<div>
<h2>画像を登録する</h2>
<form action="{% url '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>
CSS も一切利用しないシンプルな HTML のテンプレートになります…。
title
、id
、upload_form
が View から渡されることを期待しているデータです。
title
は単にページのタイトルや見出しに利用するためのデータになります。
id
はアップロードされた画像の ID です。より具体的には、レコードに自動的に割り振られる id
になります(プライマリーキー)。この id
は、アップロードされた画像の ID をユーザーに示すために使用します。
そして、この画像の ID を URL に指定してもらうことでアップロードされた画像の表示を実現します。この辺りは アップロードされた画像の表示 で解説していきます。
この id
には、アップロード前には None
を、アップロード後にはレコードの id
を指定してもらうことで、アップロード前のページとアップロード後のページを切り替えられるようにしています。
また、upload_form
は 画像アップロード用の Form の作成 で作成した UploadForm
クラスのインスタンスで、<form>
〜</form>
の間に {{ upload_form.as_p }}
と記述しておくことで、レンダリング時に画像アップロード用の ファイルを選択
ボタンを持つフォームのタグが HTML 内に埋め込まれることになります。
今回のように画像等のファイルのアップロードを行う場合、form
タグには enctype="multipart/form-data"
を指定する必要があるので注意してください。この指定がないと、画像のアップロードをうまく行うことができません。
画像アップロード用の View の作成
続いて、View を作成していきます。
この View では大きく分けて2つのことを行います。
まず、画像のアップロード用のフォームを表示するための HTML を生成します。これには Form と Template を利用します。
さらに、フォームからアップロードされた画像の保存を行います。これには Form と Model を利用します。
これらを行うビューは、下記のように upload_project/upload_app/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, 'upload_app/index.html', params)
HTML の生成
params
はテンプレートに埋め込むデータを格納した辞書です。
この辞書を render
関数に渡すことで、Template の upload_app/index.html
に params
のデータを埋め込んだ HTML を作成しています。
params
の各キーの値がどのようにテンプレートに埋め込まれるかは 画像アップロード用の Template の作成 で作成した index.html
をご参照いただければと思います。
アップロードされた画像とパスの保存
また、画像アップロード用のフォームから画像がアップロードされた際には、request.method
が 'POST'
として index
関数が実行されることになります。
この際には、アップロードされた画像のファイル保存と、その保存先のパスをフィールドとして持つレコードのデータベースへの保存を行なっています。
ただ、画像アップロード用の Model の作成 で解説したように、これらが行われるのは UploadImage
のインスタンスが save
メソッドを実行した時のはずです。
それに対し、save
メソッドを実行しているのは UploadForm
のインスタンスの form
のみです。
なぜこれでアップロードされた画像が保存されることになるのでしょうか?
結論としては、UploadForm
のインスタンスが save
メソッドを実行することで、UploadImage
のインスタンスも save
メソッドを実行することになるためです。
まず、UploadForm
のように ModelForm
を継承した Form は、データ属性として meta
の model
で指定した Model のインスタンスを持つことになります。UploadForm
の場合は UploadImage
のインスタンスを持つことになりますね!
また、UploadForm
のように ModelForm
を継承した Form の save
メソッドの中では、そのデータ属性に持つ Model の save
メソッドが実行されます。つまり、UploadForm
の場合は UploadImage
の save
メソッドが実行されることになります。
なので、UploadForm
のインスタンスに save
メソッドを実行させることで UploadImage
のインスタンスにも save
メソッドを実行させることができ、これにより画像のファイル保存と、その保存先のパスをフィールドとして持つレコードの保存が行われることになります。
アップロードされた画像からの Model のインスタンス生成
また、UploadForm
のインスタンス生成時に、引数に request.POST
と request.FILES
を指定しているところもポイントになります。
これらには、ユーザーがフォームから送信したデータが格納されています。特に request.FILES
にはアップロードされた画像ファイルのデータそのものや、アップロードされた画像ファイルのファイル名等が格納されています。
そして、これらを UploadForm
のインスタンス生成時に引数として指定することで、request.POST
と request.FILES
のデータをセットした状態の UploadImage
のインスタンスが生成されることになります。
これらのデータをセットしているため、UploadImage
のインスタンスが save
メソッドを実行した際には request.POST
や request.FILES
に格納されているデータ、すなわちユーザーからフォームで送信されたデータが保存されることになります(アップロードされた画像など)。
逆に UploadForm
のインスタンス生成時に request.POST
や request.FILES
を指定しなかった場合は、save
メソッドを実行してもユーザーから送信されたデータを保存することができないので注意してください。
保存した Model のインスタンスの取得
上記の解説の通り、UploadForm
のインスタンスが save
メソッドを実行することで画像のアップロード自体は完了するのですが、最後にユーザーに画像のアップロードが完了したことを伝えるための HTML の生成を render
関数で行っています。
この際、保存したレコードに割り振られた id
をページに表示するため、 params['id']
に upload_image.id
をセットして Template に渡すようにしています。
この upload_image
は、form
の持つ UploadImage
のインスタンス、すなわち先ほど save
メソッドを実行した UploadImage
のインスタンスになります。
UploadForm
のような ModelForm
を継承した Form のインスタンスが save
メソッドを実行することで、返却値としてデータ属性に持つ Model のインスタンスを取得することができます。
また、Model のインスタンスのデータ属性 id
には、save
メソッドの実行時により保存されたレコードの id
がセットされています(レコードの id
はデータベースへの保存時に自動的に割り振られます)。なので、それを upload_image.id
で取得して Template に渡すことで、保存したレコードに割り振られた id
の表示を実現しています。
Model を直接操作することも可能
先ほど紹介した View は Form を介して Model の操作を行なっているので、ちょっと Model がどう操作されているかが分かりにくいかもしれません。
ということで、直接 Model を操作してアップロードされた画像やレコードの保存を行う index
関数を書いてみました。それが下記となります(コードの変更はしなくても良いです)。これでも前述で紹介した index
関数と同様の保存結果を得ることができます。
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'):
upload_image = UploadImage(image=request.POST, request.FILES['image'])
upload_image.save()
params['id'] = upload_image.id
return render(request, 'upload_app/index.html', params)
is_valid
メソッドで行っていたバリデーション(妥当性の検証)は省略してしまっているものの、上記を見れば、前述の index
関数で Form を介して Model にどのような操作が行われているがイメージしやすくなるのではないかと思います。
画像アップロード用の View と URL の関連付け
View も出来上がったので、最後に URL と upload_project/upload_app/views.py
の index
関数の関連付けを行なっておきます。まずは、upload_project/upload_app/urls.py
を下記のように作成します(新規作成する必要があります)。
from django.urls import path
from . import views
urlpatterns = [
path('', views.index, name='index'),
]
さらに、upload_project/upload_project/urls.py
を下記のように変更します。
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('upload_app/', include('upload_app.urls'))
]
上記により、http://localhost:8000/upload_app/
へのリクエストがあった際に upload_project/upload_app/views.py
の index
関数が実行されるようになります。
スポンサーリンク
アップロードの動作確認
ここまでの変更により、画像のアップロードを行うことができるようになったはずです。
なので、ここで画像のアップロードの動作確認を行なってみましょう!
まず、upload_project/
の中で下記の2つのコマンドを実行してマイグレーションを行います。これにより、画像アップロード用の Model の作成 で作成した UploadImage
の持つフィールドに合わせたテーブルがデータベースに作成されます。
% python manage.py makemigrations % python manage.py migrate
続いて、upload_project/
の中で下記コマンドを実行し、Django の開発用ウェブサーバーを起動します。
% python manage.py runserver
さらにウェブブラウザを開いてアドレスバーに下記 URL を入力してエンターキーを押します。
http://localhost:8000/upload_app/
すると、下の図のような画面が表示されると思います。
ここで、ファイルを選択
ボタンをクリックし、表示されるダイアログから画像ファイルの選択を行います。選択が完了すれば アップロード
ボタンをクリックします。
すると、下の図のような画面に遷移するはずです。
ここで、upload_project/media/img/
のフォルダの中を見てみてください。ここに先ほど追加した画像ファイルが追加されていれば、画像のアップロードに成功していることになります!
同様の操作で画像をアップロードするたびに、upload_project/media/img/
に画像がファイルが保存されていくことが確認できると思います。もし、同じ名前の画像ファイルをアップロードした場合でも、Django 内でファイル名に適当な文字列を追加した状態で保存されるようになっているため、保存したファイルが上書きされることは無いはずです。
もう忘れてしまっているかもしれませんが、upload_project/media/
は 画像のアップロード先フォルダの設定 で MEDIA_ROOT
に設定したフォルダになります。
import os
MEDIA_ROOT = os.path.join(BASE_DIR, 'media/')
さらに、img/
は 画像アップロード用の Model の作成 で ImageField
を Model に持たせる際に upload_to
で指定したフォルダになります。
from django.db import models
class UploadImage(models.Model):
image = models.ImageField(upload_to='img/')
こんな感じで、アップロードした画像のファイルは MEDIA_ROOT
で設定したフォルダの中の upload_to
で設定したフォルダに保存されていくことになります。
ですので、これらを変更することでアップロードされた画像の保存先を変更することが可能です。
また、もしデータベースのテーブルを表示することができるのであれば、上記手順で画像をアップロードした後にテーブル upload_app_uploadimage
を表示してみて下さい。
image
の列に “保存されたファイルの MEDIA_ROOT
からの相対パス” を持つレコードが保存されていることが確認できるはずです。この結果から、画像ファイルだけでなく、その画像ファイルのパスもデータベースに保存されていることが確認できると思います。
といっても、ここまで作成してきたアプリで出来るのはこれだけです。
ただ、アップロードされた画像がファイル保存され、さらにその保存先のパスも保存されているので、あとはこれらを利用して画像を取得し、その画像を加工したり、その画像を表示したりすることも可能になったことになります。
次は、このアップロードされた画像を表示する手順を、先ほど作成したアプリに手を加えながら説明していきたいと思います。
ちなみに、先ほどデータベースのテーブルをお見せしましたが、あれは VSCode の SQLite Viewer
というプラグインを利用して表示した結果になります。これを使えば簡単にテーブルの中身が確認できるようになるのでオススメです。使い方等は下記ページで解説していますので、詳しく知りたい方は是非読んでみて下さい。
アップロードされた画像の表示
次は、先ほど作成したアプリに手を加えながら、アップロードされた画像の表示を実現する手順を解説していきたいと思います。
アップロードされた画像の表示といっても、アップロードされた画像はファイルとして保存されているわけですから、基本的に下記ページで解説している静的ファイルを扱う際と同様の考え方で表示を実現することが可能です。
【Django】静的ファイルの扱い方(画像・CSS・JS)ただ、リクエストされた URL がアップロードファイルに対するものであるかの判断の仕方や、ファイルの取得先のフォルダの設定の仕方などが静的ファイルの時とは異なるため、その辺りを中心に解説していきたいと思います
アップロードファイルをレスポンスで返却
まず前提として、静的ファイルやアップロードファイルへのリクエストがあった際に、リクエストされたファイルをレスポンスとして返却するのはウェブサーバー(ソフト)の役目となります。
開発時には、このウェブサーバーが Django の開発用ウェブサーバーとなります。
この辺りは先ほど紹介した「静的ファイルの扱い方」の解説ページでも解説しているので、詳しく知りたい方は 「静的ファイルの扱い方」のページ をご参照ください。
で、アップロードファイルの表示を実現するためには、ウェブサーバーがファイルをレスポンスとして返却する流れをアップロードファイルに対しても実現する必要があります。
以降では、まずは静的ファイルのレスポンスの流れを整理し、その後にアップロードファイルのレスポンスを実現するために必要になる設定を解説していきたいと思います。
ただ、以降の解説は Django 4 に基づいたものになりますので注意して下さい。
Django 3 以前のバージョンを利用されている方は、ここからの解説では /MEDIA_URL
を MEDIA_URL
、/STATIC_URL
を STATIC_URL
にそれぞれ置き換えて読み進めていただければと思います。これで話が合うはずです(最初の /
が不要になる)。
この理由は、次の MEDIA_URL の定義 を読んでいただければ分かると思います
静的ファイルをレスポンスで返却する流れ
静的ファイルの場合、ファイルへのリクエストがあった際に、Django の開発用ウェブサーバーは URL からリクエストが静的ファイルに対するものであるかどうかを判断します。
ファイルのリクエストは、ウェブサーバーから送信した HTML 内に <img>
や <link>
などのファイルを扱うタグが存在する場合、ウェブブラウザから送信されます。
そして、リクエストが静的ファイルに対するものであると判断した場合、Django の開発用ウェブサーバーは特定のフォルダから静的ファイルを取得し、それをレスポンスとしてウェブブラウザ(クライアント)に返却をします。
より具体的には、Django の開発用ウェブサーバーは、リクエストされた URL が /STATIC_URL
から始まる場合、そのリクエストを静的ファイルに対するものであると判断し、その際には「検索先フォルダ」となる特定のフォルダから取得するようになっています(STATIC_URL
は settings.py
で設定される変数です)。
そして、取得したファイルがレスポンスとしてウェブブラウザに返却され、そのファイルをウェブブラウザが読み込んでページに反映することになります。これによって、画像の場合は画像がページに表示されることになります。
アップロードファイルをレスポンスで返却するために必要な設定
アップロードされた画像を表示するためには、アップロードファイルへのリクエストがあった際にも、上記のような動作を Django の開発用ウェブサーバーに行わせる必要があります。
具体的には、ファイルへのリクエストがあった際に、Django の開発用ウェブサーバーに “リクエストがアップロードファイルに対するものであるかどうか” を判断できるようにする必要があります。
静的ファイルの場合は URL が /STATIC_URL
から始まる場合に “静的ファイルへのリクエストである” と判断していましたが、アップロードファイルの場合は URL が /MEDIA_URL
から始まる場合に “アップロードファイルへのリクエストである” と判断するようにしていきます。
この MEDIA_URL
は初期状態では定義されていませんので、別途 settings.py
で定義する必要があります。
そして、リクエストがアップロードファイルに対するものであると判断した場合、Django の開発用ウェブサーバーに特定のフォルダからアップロードファイルを取得できるようにする必要があります。
で、アップロードファイルは前述の通り MEDIA_ROOT
以下に保存されるようになっていますので、MEDIA_ROOT
からアップロードファイルを取得できるようにする必要があることになります。
なんだか難しそうですが、これらは下記の2つを行うだけで設定することができます。
MEDIA_URL
を定義するMEDIA_URL
とMEDIA_ROOT
の関連付け
後者が、URL が /MEDIA_URL
から始まる場合に MEDIA_ROOT
からファイルを取得するようにするための Django 開発用ウェブサーバーの設定となります。
関連付けと聞くと曖昧に感じるかもしれませんが、要は urls.py
で行う “URL と実行する View の関数” との関連付けと同様の設定になります。
そして、それを設定するために、前者の MEDIA_URL
の定義が必要になります。
また、上記のレスポンスの流れはあくまでも Django の開発用ウェブサーバーを使用している場合の流れです。本番環境では Django 開発用ウェブサーバではなく、Apache や Nginx 等のウェブサーバーを利用することになります。
ただ、設定方法が異なるだけであり、実現すべきことは Django の開発用ウェブサーバ でも Apache・Nginx 等でも変わりません。
この辺りについても、簡単にですが、後述の 本番環境でのアップロードファイルの扱い方 で解説させていただこうと思います。
MEDIA_URL
の定義
では、アップロードされた画像の表示を実現するために、まずは MEDIA_URL
を定義していきたいと思います。
前述の通り、この MEDIA_URL
はアップロードファイルへのリクエストであるかどうかを判断するための情報となります。具体的には、リクエストされた URL が /MEDIA_URL
から始まる場合に、アップロードファイルへのリクエストであると判断されるようになります(次の MEDIA_URL と MEDIA_ROOT の関連付け を行えば)。
では、MEDIA_URL
を定義していきましょう!
今回は MEDIA_URL
を 'media/'
として定義していきたいと思います。このために、upload_project/upload_project/settings.py
に下記を追記します。画像のアップロード先フォルダの設定 で MEDIA_ROOT
の定義を行なっていますので、その定義の直後に追記して下さい。
MEDIA_URL = 'media/'
ただし、上記は Django 4 での MEDIA_URL
の定義例となります。Django 3 以前を利用されている方は下記のように MEDIA_URL
を定義して下さい。
MEDIA_URL = '/media/'
要は Django 3 以前の場合は先頭に /
を付ける必要があります。この違いがあるために、Django 3 以前のバージョンを利用されている方には /MEDIA_URL
を MEDIA_URL
と置き換えて解説を読み進めていただいています。
MEDIA_URL
と MEDIA_ROOT
の関連付け
続いて、リクエストされた URL が /MEDIA_URL
から始まる場合に MEDIA_ROOT
からファイルを取得するよう Django 開発用ウェブサーバーの設定を行います。
これは、 upload_project/upload_project/url.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('admin/', admin.site.urls),
path('upload_app/', include('upload_app.urls'))
]
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
上記に関しては、Django 4 でも Django 3 以前でも同じように記述してオーケーです。
以上の設定により、リクエストの URL が /MEDIA_URL
から始まる場合に、Django 開発用ウェブサーバーが MEDIA_ROOT
からファイルを取得してレスポンスとして返却してくれるようになります。
より具体的には、URL が /MEDIA_URL{ファイルのパス}
である場合、取得されるのは MEDIA_ROOT{ファイルのパス}
のファイルとなります。
ここまでの MEDIA_URL
と MEDIA_ROOT
の定義内容を考慮すれば、URL が /media/{ファイルのパス}
である場合に、upload_project/media/{ファイルのパス}
のファイルが取得されることになりますね!
ですので、リクエストする URL の {ファイルのパス}
部分に MEDIA_ROOT
以下に存在するファイルの相対パス(MEDIA_ROOT
からの相対パス)を指定してやれば、アップロードされた画像ファイルがレスポンスとして返却されることになります。
で、画像アップロード用の Model の作成 等で解説したように、このパスはデータベースに保存されていますので、データベースから取得したパスを利用することで、アップロードされた画像ファイルを取得可能な URL を作成することができます。
といっても、この URL は ImageField
を持つ Model であれば MEDIA_URL
の定義に合わせて自動的に生成してくれます。なので、その URL を取得すれば良いだけです。
そして、<img>
タグの src
属性に Model から取得した URL を指定しておけば、HTML を受け取ったウェブブラウザが src
属性に指定された URL をリクエストしてくれるようになります。さらに、レスポンスとして受け取った画像ファイルをウェブブラウザがページ上に表示してくれます。
また、HTML のウェブブラウザへの送信は Django の開発用ウェブサーバーが行ってくれます。
そのため、あとは HTML を生成するように View や Template 等を作成し、さらにその HTML に Model から取得した URL を src
属性に指定した <img>
タグを持たせるようにすれば、アップロードされた画像の表示を実現することができることになります。
スポンサーリンク
画像の表示用の Template の作成
ということで、ここからは HTML を生成するための View と Template を作成していきます。
まずは Template を作成していきましょう!
ポイントは、<img>
タグを持つ HTML を作成するようにすること、さらに、その <img>
タグの src
属性に Model から取得した URL を指定するところになります。
ただ、今回は Model からの URL の取得は View に行わせるようにしたいと思います。そのため、Template は単に View から URL を受け取るようにすれば良いだけです。
今回は、画像表示用の Template として、下記の upload_project/upload_app/templates/upload_app/preview.html
を作成したいと思います(新規作成が必要なファイルになります)。
<!doctype html>
<html lang="ja">
<head>
<meta charset="utf-8">
<title>{{title}}</title>
</head>
<body>
<h1>{{title}}</h1>
<div>
<h2>ID:{{id}}の画像</h2>
<img src="{{url}}">
</div>
</body>
</html>
この Template 自体はかなり簡単だと思います。
View から title
、id
、url
を受け取ることを期待しており、前者の2つに関しては 画像アップロード用のテンプレートを作成 でも使用していたので説明は省略します。
また、url
としては /MEDIA_URL/{ファイルのパス}
の形式の URL、すなわち Model から取得された URL を受け取ることを期待しています。
画像表示用の View の作成
続いて、先ほど作成した Template に Model から取得した URL を渡すために、画像表示用の View として下記のように preview
関数を upload_project/upload_app/views.py
に追加します。
index
関数は 画像アップロード用の View の作成 から変更していません。get_object_or_404
を import
している点に注意してください。
from django.shortcuts import render, get_object_or_404
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, 'upload_app/index.html', params)
def preview(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, 'upload_app/preview.html', params)
preview
関数では、最初に get_object_or_404
を実行してデータ属性 id
が image_id
と一致する UploadImage
のインスタンスを取得しています。
画像アップロード用の Model の作成 でも説明したように、Model のインスタンスはデータベースのレコードと対応しており、このインスタンスの取得は id
が image_id
であるレコードをデータベースから取得するようなものです。そして、取得された Model には、レコードの各フィールドの値がデータ属性としてセットされています。
そして、UploadImage
のように ImageField
を持たせた Model の場合、データ属性から /MEDIA_URL/{ファイルのパス}
の形式 URL を取得することができます。
より具体的には、UploadImage
は ImageField
を image
データ属性として持たせており、この image
の image.url
からその URL を取得することができます。
なので、この image.url
を params
の 'url'
キーに指定して先ほど作成した Template に渡してやれば、/MEDIA_URL/{ファイルのパス}
の形式の URL を src
属性に指定した <img>
タグを持つ HTML を生成できることになります。
こんな感じで、画像アップロード用の Model の作成 で作成した UploadImage
のように、ImageField
を持たせたモデルを利用すれば、画像ファイルのアップロードの実現だけでなく、アップロードされた画像をリクエストするための URL の取得も簡単に実現できます。
画像表示用の View と URL の関連付け
最後に作成した画像表示用のビューと URL との関連付けを行いましょう。
このために、upload_project/upload_app/urls.py
を下記のように変更します。
from django.urls import path
from . import views
urlpatterns = [
path('', views.index, name='index'),
path('preview/<int:image_id>/', views.preview, name='preview'),
]
上記のように変更することで、http://localhost:8000/upload_app/preview/ID/
に対するリクエストがあった場合に upload_project/upload_app/views.py
の preview
関数が実行される事になります。
また、preview
関数の引数 image_id
には、URL に指定した ID が渡される事になります。この ID は、前述の通り画像のアップロード時にページ上に表示されますので、そこから確認することができます。
アップロード画像の表示の動作確認
次は、ここまで開発してきたアプリを利用して、アップロード画像の表示ができることを確認しておきましょう!
まずは、下記コマンドを upload_project
フォルダで実行し、Django の開発用ウェブサーバーを起動します(マイグレーションは アップロードの動作確認 で行っているはずなのでここでは不要です)。
% python manage.py runserver
続いて、ウェブブラウザのアドレスバーに下記 URL を指定し、表示されるページで画像のアップロードを行なって下さい。
http://localhost:8000/upload_app/
アップロードが完了すると ID が表示されるので、その ID を用いてアドレスバーに下記 URL を指定します(ID 部分はご自身で確認した ID を指定して下さい)。
http://localhost:8000/upload_app/preview/ID/
すると、下の図のようなページに遷移し、事前にアップロードした画像が表示されることを確認できると思います。
以上で、画像のアップロードからアップロードした画像の表示までの一連の動作を確認できたことになります!
たったこれだけですが、画像のアップロード及びアップロードされた画像の表示ができるようになったことになりますので、開発できるウェブアプリの幅は大きく広がったと思います。
スポンサーリンク
本番環境でのアップロードファイルの扱い方
最後に、本番環境でのアップロードファイルの扱い方について簡単に解説しておきます。
本番環境用のウェブサーバーの設定が必要
MEDIA_URL と MEDIA_ROOT の関連付け で static
関数を利用して /MEDIA_URL
から始まる URL のリクエストがあった際に MEDIA_ROOT
以下からファイルを取得してレスポンスするよう Django の開発用ウェブサーバーの設定を行いました。
ただ、ここで利用した static
関数は、settings.py
で Debug = False
を設定した場合は無効化されてしまいますので注意してください(空のリスト返却するだけの関数になる)。
そもそも、MEDIA_URL と MEDIA_ROOT の関連付け で行った設定は、あくまでも Django の開発用ウェブサーバー向けの設定になります。
ウェブでアプリを公開するような本番環境では、Django の開発用ウェブサーバーを使用せず、Apache や Nginx 等のウェブサーバーを利用するのが一般的です。
なので、MEDIA_URL と MEDIA_ROOT の関連付け で Django の開発用ウェブサーバーに対して行った設定と同等の設定を、Apache や Nginx に対して設定する必要があります。
設定内容に関しては、静的ファイルを扱う際とほぼ同じになると思います。ですので、下記ページの 本番環境でのアップロードファイルの扱い方 が参考になると思います。
【Django】静的ファイルの扱い方(画像・CSS・JS)ただ、静的ファイルを扱う際とは下記が異なるので注意して下さい。
collectstatic
は行う必要なし
collectstatic
を実行してファイルを一箇所に集めるような操作は不要です。なぜなら、アップロードファイルの場合、すでにアップロードしたファイルは MEDIA_ROOT
以下に集まっているからです。
スポンサーリンク
MEDIA_URL
と MEDIA_ROOT
に基づいて設定を行う
また、上記ページでは Nginx の設定例を紹介していますが、静的ファイルの場合は STATIC_URL
と STATIC_ROOT
に基づいて設定を行う必要があるのに対し、アップロードファイルの場合は、下記のように MEDIA_URL
と MEDIA_ROOT
に基づいて設定を行う必要があります。
server {
〜略〜
location /MEDIA_URL {
alias MEDIA_ROOT;
}
〜略〜
}
少なくとも Nginx では、上記のように設定を行うことでアップロードファイルを表示できるになることを確認しています。
また、いずれ機会があれば、Django での本番環境に移行する際の一連の手順をまとめて紹介していきたいと思います。
まとめ
このページでは、Django での「画像のアップロード」について解説しました!
画像のアップロードを行う上でのポイントは ImageField
を持たせた Model を作成することです。
このような Model を作成することで、画像のアップロードを実現するにあたって必要になる下記を簡単に実現することができます。
- 画像アップロード用のフォームの作成
- アップロードされた画像の保存
- 保存された画像の情報の管理
また、アップロードされた画像を表示する際には、/MEDIA_URL
から始まる URL がリクエストされた際にウェブサーバーに MEDIA_ROOT
以下のファイルを取得するように設定するところがポイントになります。
ここを理解しておけば、もちろん設定方法は異なるものの、開発環境でも本番環境でもアップロードされた画像の表示を実現しやすくなると思います!
今回は単にアップロードされた画像の表示のみを行いましたが、下記ページではアップロードされた画像に対して画像処理を行う手順を解説しています。画像処理を行うウェブアプリを実現してみたいという方は是非読んでみてください!
Django で簡単な画像処理を行う(PIL 利用)また、アップロードされた画像を削除する手順は下記ページで解説しています。今回紹介した手順でアップロードした画像をアプリから削除できるようにしたい場合は、是非このページを参考にしてください!
【Django】アップロードされた画像を削除するおすすめ書籍(広告)
このページに興味を持たれた方であれば、下記の Django4 Webアプリ開発 実装ハンドブック がオススメです。
このページでは単に画像のアップロードの仕方について解説を行いましたが、上記の Django4 Webアプリ開発 実装ハンドブック では「会員制フォトギャラリー」アプリの開発の仕方について解説されており、その中で今回紹介した画像のアップロード機能の有効な活用方法を学ぶことができます。
また、画像のアップロードだけでなく、ユーザー管理(ログインなど)についてもアプリを開発しながら学ぶことができます。他にもブログアプリや問い合わせページ(メールの送信機能)の作り方などについても解説されており、Django 入門者の方が Django で出来ることの幅を広げるのに最適な書籍だと思います。
また、Django 4 向けの解説書籍であり、2022/5/30 時点だとおそらく Django 向け書籍としてはトップレベルに情報が新しいです。
書籍の難易度としては、Django 初心者の方向けというよりは、Django の入門書一冊くらいを一通り学んだことのある方向けだと思います。
「Django の入門書を一通り読んだけど次に何をすれば良いかわからない…」という方は是非読んでみてください!