【Django】画像をアップロードする

Djangoでの画像のアップロードの実現方法解説ページアイキャッチ

このページにはプロモーションが含まれています

このページでは、Django での「画像アップロード」を行うための手順について解説していきます。

単に手順だけを説明するのではなく、実際に画像アップロード可能なウェブアプリを開発しながら、画像アップロードを行う手順を解説していきたいと思います。

画像のアップロードの仕方を理解しておくことで開発できるウェブアプリの幅が広がります。実際 Twitter や Instagram 等でも画像のアップロードができますよね!

今回紹介するアプリは簡単なものですが、Django での画像のアップロードにも慣れることができると思いますし、今後のウェブアプリ開発に役に立つ点もあると思いますので、是非手を動かしながら読み進めてみてください!

また、アップロードされた画像ではなく、静的ファイルとして用意した画像の扱いについては下記ページで解説しています。アップロードされた画像であっても、画像の表示の仕方などは静的ファイルの時と同様の点も多いですので、こちらも是非読んでみてください。

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での静的ファイルの扱い方の解説ページアイキャッチ 【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.pyINSTALLED_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
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.pyupload_app フォルダと同階層の media フォルダということですね!

この MEDIA_ROOT のフォルダは画像アップロード時に自動的に作成されるので、フォルダを手動で作成する必要はありません。

画像アップロード用の Model の作成

ここからは、アプリ側のファイルの変更を行なっていきます(upload_project/upload_app 以下のファイル)。

まずは Model を作成していきます。

前述の通り、Model を作成する上でポイントになるのが、ImageField を持たせておくことになります。

ということで、ImageField を持つ Model を作成していきます。具体的には、下記のように upload_project/upload_app/models.py を変更します。これにより、ImageField を持った Model の UploadImage が定義されることになります。

UploadImageの定義
from django.db import models

class UploadImage(models.Model):
    image = models.ImageField(upload_to='img/')

この ImageField を持った Model には下記のような特徴があります。

まず前提として、マイグレーションを行うと、Model の持つフィールドに合わせたテーブルがデータベースに作成されます。そして、Model のインスタンスに save メソッドを実行させれば、作成されたテーブルに合わせたレコードがデータベースに保存されます(プライマリーキーを指定していない場合、id が自動的に割り振られることになります)。

Modelのインスタンスを保存することでレコードが保存される様子

ImageField を持つ Model の場合は少し特殊で、インスタンスにアップロードされた画像をセットした状態で save メソッドを実行させた場合、データベースへのレコードの保存だけでなく、アップロードされた画像のファイル保存も行われます。そして、ファイルの保存先のフォルダは MEDIA_ROOT 以下となります。

また、上記のように ImageFieldupload_to を指定することで、アップロードされた画像を MEDIA_ROOT 以下のどのフォルダに保存するかを指定することができます。上記のように upload_to='img/' を指定すれば、MEDIA_ROOT のフォルダにある img フォルダの中にアップロードされた画像がファイル保存されることになります。

ImageFieldを持つModelのインスタンスを保存することで画像ファイルが保存される様子

さらに、画像ファイルが保存されるのと同時に、そのファイルの保存先のパスをフィールドとして持つレコードがデータベースに保存されます。より具体的には、保存された画像ファイルの “MEDIA_ROOT からの相対パス” をフィールドとして持つレコードがデータベースに保存されます。

ImageFieldを持つModelのインスタンスを保存することで画像のパスをフィールドに持つレコードが保存される様子

今回用意した 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 は手動で新規作成する必要があります)。

UploadForm
from django import forms
from .models import UploadImage

class UploadForm(forms.ModelForm):
    class Meta:
        model = UploadImage
        fields = ['image']

上記のように ModelForm を継承する形で Form を定義すれば、Metamodel で指定した Model 用の Form を作成することができます。さらに、その Form で扱う入力フォームは fields で指定したもののみとなります。

上記では model = UploadImagefields = ['image'] を指定しているので、UploadFormUploadImageimage 用の入力フォームを扱う Form ということになります。さらに、image の実体は ImageField であり、この入力フォームは画像アップロード用の ファイルを選択 ボタンとなります。

つまり、上記の UploadForm のインスタンスを生成すれば、画像アップロード用の ファイルを選択 ボタンを持つフォームを作成することができます。

ImageFiledから作成される画像アップロード用のフォーム

あとは、このインスタンスを 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 のテンプレートになります…。

titleidupload_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.htmlparams のデータを埋め込んだ HTML を作成しています。

params の各キーの値がどのようにテンプレートに埋め込まれるかは 画像アップロード用の Template の作成 で作成した index.html をご参照いただければと思います。

アップロードされた画像とパスの保存

また、画像アップロード用のフォームから画像がアップロードされた際には、request.method'POST' として index 関数が実行されることになります。

この際には、アップロードされた画像のファイル保存と、その保存先のパスをフィールドとして持つレコードのデータベースへの保存を行なっています。

ただ、画像アップロード用の Model の作成 で解説したように、これらが行われるのは UploadImage のインスタンスが save メソッドを実行した時のはずです。

それに対し、save メソッドを実行しているのは  UploadForm のインスタンスの form のみです。

なぜこれでアップロードされた画像が保存されることになるのでしょうか?

結論としては、UploadForm のインスタンスが save メソッドを実行することで、UploadImage のインスタンスも save メソッドを実行することになるためです。

まず、UploadForm のように ModelForm を継承した Form は、データ属性として metamodel で指定した Model のインスタンスを持つことになります。UploadForm の場合は UploadImage のインスタンスを持つことになりますね!

また、UploadForm のように ModelForm を継承した Form の save メソッドの中では、そのデータ属性に持つ Model の save メソッドが実行されます。つまり、UploadForm の場合は UploadImagesave メソッドが実行されることになります。

なので、UploadForm のインスタンスに save メソッドを実行させることで UploadImage のインスタンスにも save メソッドを実行させることができ、これにより画像のファイル保存と、その保存先のパスをフィールドとして持つレコードの保存が行われることになります。

FormのsaveメソッドからModelのsaveメソッドが実行される様子

アップロードされた画像からの Model のインスタンス生成

また、UploadForm のインスタンス生成時に、引数に request.POSTrequest.FILES を指定しているところもポイントになります。

これらには、ユーザーがフォームから送信したデータが格納されています。特に request.FILES にはアップロードされた画像ファイルのデータそのものや、アップロードされた画像ファイルのファイル名等が格納されています。

そして、これらを UploadForm のインスタンス生成時に引数として指定することで、request.POSTrequest.FILES のデータをセットした状態の UploadImage のインスタンスが生成されることになります。

これらのデータをセットしているため、UploadImage のインスタンスが save メソッドを実行した際には request.POSTrequest.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.pyindex 関数の関連付けを行なっておきます。まずは、upload_project/upload_app/urls.py を下記のように作成します(新規作成する必要があります)。

アプリ内での関数とURLの関連付け
from django.urls import path
from . import views

urlpatterns = [
    path('', views.index, name='index'),
]

さらに、upload_project/upload_project/urls.py を下記のように変更します。

アプリとURLの関連付け
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.pyindex 関数が実行されるようになります。

スポンサーリンク

アップロードの動作確認

ここまでの変更により、画像のアップロードを行うことができるようになったはずです。

なので、ここで画像のアップロードの動作確認を行なってみましょう!

まず、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 で指定したフォルダになります。

UploadImageの定義
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 というプラグインを利用して表示した結果になります。これを使えば簡単にテーブルの中身が確認できるようになるのでオススメです。使い方等は下記ページで解説していますので、詳しく知りたい方は是非読んでみて下さい。

VSCodeでデータベースのテーブルの中身を表示する方法の解説ページアイキャッチ 【Django/VSCode】データベースのテーブルの中身を表示する

アップロードされた画像の表示

次は、先ほど作成したアプリに手を加えながら、アップロードされた画像の表示を実現する手順を解説していきたいと思います。

アップロードされた画像の表示といっても、アップロードされた画像はファイルとして保存されているわけですから、基本的に下記ページで解説している静的ファイルを扱う際と同様の考え方で表示を実現することが可能です。

Djangoでの静的ファイルの扱い方の解説ページアイキャッチ 【Django】静的ファイルの扱い方(画像・CSS・JS)

ただ、リクエストされた URL がアップロードファイルに対するものであるかの判断の仕方や、ファイルの取得先のフォルダの設定の仕方などが静的ファイルの時とは異なるため、その辺りを中心に解説していきたいと思います

アップロードファイルをレスポンスで返却

まず前提として、静的ファイルやアップロードファイルへのリクエストがあった際に、リクエストされたファイルをレスポンスとして返却するのはウェブサーバー(ソフト)の役目となります。

開発時には、このウェブサーバーが Django の開発用ウェブサーバーとなります。

この辺りは先ほど紹介した「静的ファイルの扱い方」の解説ページでも解説しているので、詳しく知りたい方は 「静的ファイルの扱い方」のページ をご参照ください。

で、アップロードファイルの表示を実現するためには、ウェブサーバーがファイルをレスポンスとして返却する流れをアップロードファイルに対しても実現する必要があります。

以降では、まずは静的ファイルのレスポンスの流れを整理し、その後にアップロードファイルのレスポンスを実現するために必要になる設定を解説していきたいと思います。

ただ、以降の解説は Django 4 に基づいたものになりますので注意して下さい。

Django 3 以前のバージョンを利用されている方は、ここからの解説では /MEDIA_URLMEDIA_URL/STATIC_URLSTATIC_URL にそれぞれ置き換えて読み進めていただければと思います。これで話が合うはずです(最初の / が不要になる)。

この理由は、次の MEDIA_URL の定義 を読んでいただければ分かると思います

静的ファイルをレスポンスで返却する流れ

静的ファイルの場合、ファイルへのリクエストがあった際に、Django の開発用ウェブサーバーは URL からリクエストが静的ファイルに対するものであるかどうかを判断します。

ファイルのリクエストは、ウェブサーバーから送信した HTML 内に <img><link> などのファイルを扱うタグが存在する場合、ウェブブラウザから送信されます。

そして、リクエストが静的ファイルに対するものであると判断した場合、Django の開発用ウェブサーバーは特定のフォルダから静的ファイルを取得し、それをレスポンスとしてウェブブラウザ(クライアント)に返却をします。

静的ファイルへのリクエストに対するレスポンスの流れを示した図

より具体的には、Django の開発用ウェブサーバーは、リクエストされた URL が /STATIC_URL から始まる場合、そのリクエストを静的ファイルに対するものであると判断し、その際には「検索先フォルダ」となる特定のフォルダから取得するようになっています(STATIC_URLsettings.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_URL = 'media/'

ただし、上記は Django 4 での MEDIA_URL の定義例となります。Django 3 以前を利用されている方は下記のように MEDIA_URL を定義して下さい。

MEDIA_URLの定義(Django3)
MEDIA_URL = '/media/'

要は Django 3 以前の場合は先頭に / を付ける必要があります。この違いがあるために、Django 3 以前のバージョンを利用されている方には /MEDIA_URLMEDIA_URL と置き換えて解説を読み進めていただいています。

MEDIA_URL と MEDIA_ROOT の関連付け

続いて、リクエストされた URL が /MEDIA_URL から始まる場合に MEDIA_ROOT からファイルを取得するよう Django 開発用ウェブサーバーの設定を行います。

これは、 upload_project/upload_project/url.py を下記のように変更することで設定することができます。

MEDIA_URLとMEDIA_ROOTの関連付け
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_URLMEDIA_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 から titleidurl を受け取ることを期待しており、前者の2つに関しては 画像アップロード用のテンプレートを作成 でも使用していたので説明は省略します。

また、url としては /MEDIA_URL/{ファイルのパス} の形式の URL、すなわち Model から取得された URL を受け取ることを期待しています。

画像表示用の View の作成

続いて、先ほど作成した Template に Model から取得した URL を渡すために、画像表示用の View として下記のように preview 関数を upload_project/upload_app/views.py に追加します。

index 関数は 画像アップロード用の View の作成 から変更していません。get_object_or_404import している点に注意してください。

画像表示用の View
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 を実行してデータ属性 idimage_id と一致する UploadImage のインスタンスを取得しています。

画像アップロード用の Model の作成 でも説明したように、Model のインスタンスはデータベースのレコードと対応しており、このインスタンスの取得は idimage_id であるレコードをデータベースから取得するようなものです。そして、取得された Model には、レコードの各フィールドの値がデータ属性としてセットされています。

get_object_or_404で指定したidのレコードが取得できる様子

そして、UploadImage のように ImageField を持たせた Model の場合、データ属性から /MEDIA_URL/{ファイルのパス} の形式 URL を取得することができます。

より具体的には、UploadImageImageFieldimage データ属性として持たせており、この imageimage.urlからその URL を取得することができます。

なので、この image.urlparams'url' キーに指定して先ほど作成した Template に渡してやれば、/MEDIA_URL/{ファイルのパス} の形式の URL を src 属性に指定した <img> タグを持つ HTML を生成できることになります。

こんな感じで、画像アップロード用の Model の作成 で作成した UploadImage のように、ImageField を持たせたモデルを利用すれば、画像ファイルのアップロードの実現だけでなく、アップロードされた画像をリクエストするための URL の取得も簡単に実現できます。

画像表示用の View と URL の関連付け

最後に作成した画像表示用のビューと URL との関連付けを行いましょう。

このために、upload_project/upload_app/urls.py を下記のように変更します。

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.pypreview 関数が実行される事になります。

また、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.pyDebug = False を設定した場合は無効化されてしまいますので注意してください(空のリスト返却するだけの関数になる)。

そもそも、MEDIA_URL と MEDIA_ROOT の関連付け で行った設定は、あくまでも Django の開発用ウェブサーバー向けの設定になります。

ウェブでアプリを公開するような本番環境では、Django の開発用ウェブサーバーを使用せず、Apache や Nginx 等のウェブサーバーを利用するのが一般的です。

なので、MEDIA_URL と MEDIA_ROOT の関連付け で Django の開発用ウェブサーバーに対して行った設定と同等の設定を、Apache や Nginx に対して設定する必要があります。

設定内容に関しては、静的ファイルを扱う際とほぼ同じになると思います。ですので、下記ページの 本番環境でのアップロードファイルの扱い方 が参考になると思います。

Djangoでの静的ファイルの扱い方の解説ページアイキャッチ 【Django】静的ファイルの扱い方(画像・CSS・JS)

ただ、静的ファイルを扱う際とは下記が異なるので注意して下さい。

collectstatic は行う必要なし

collectstatic を実行してファイルを一箇所に集めるような操作は不要です。なぜなら、アップロードファイルの場合、すでにアップロードしたファイルは MEDIA_ROOT 以下に集まっているからです。

スポンサーリンク

MEDIA_URLMEDIA_ROOT に基づいて設定を行う

また、上記ページでは Nginx の設定例を紹介していますが、静的ファイルの場合は STATIC_URLSTATIC_ROOT に基づいて設定を行う必要があるのに対し、アップロードファイルの場合は、下記のように MEDIA_URLMEDIA_ROOT に基づいて設定を行う必要があります。

Nginxの設定例
server {
    〜略〜

    location /MEDIA_URL {
        alias MEDIA_ROOT;
    }

    〜略〜
}

少なくとも Nginx では、上記のように設定を行うことでアップロードファイルを表示できるになることを確認しています。

また、いずれ機会があれば、Django での本番環境に移行する際の一連の手順をまとめて紹介していきたいと思います。

まとめ

このページでは、Django での「画像のアップロード」について解説しました!

画像のアップロードを行う上でのポイントは ImageField を持たせた Model を作成することです。

このような Model を作成することで、画像のアップロードを実現するにあたって必要になる下記を簡単に実現することができます。

  • 画像アップロード用のフォームの作成
  • アップロードされた画像の保存
  • 保存された画像の情報の管理

また、アップロードされた画像を表示する際には、/MEDIA_URL から始まる URL がリクエストされた際にウェブサーバーに MEDIA_ROOT 以下のファイルを取得するように設定するところがポイントになります。

ここを理解しておけば、もちろん設定方法は異なるものの、開発環境でも本番環境でもアップロードされた画像の表示を実現しやすくなると思います!

今回は単にアップロードされた画像の表示のみを行いましたが、下記ページではアップロードされた画像に対して画像処理を行う手順を解説しています。画像処理を行うウェブアプリを実現してみたいという方は是非読んでみてください!

Djangoでの画像処理の行い方の解説ページアイキャッチ Django で簡単な画像処理を行う(PIL 利用)

また、アップロードされた画像を削除する手順は下記ページで解説しています。今回紹介した手順でアップロードした画像をアプリから削除できるようにしたい場合は、是非このページを参考にしてください!

【Django】アップロードされた画像を削除する

おすすめ書籍(広告)

このページに興味を持たれた方であれば、下記の Django4 Webアプリ開発 実装ハンドブック がオススメです。

このページでは単に画像のアップロードの仕方について解説を行いましたが、上記の Django4 Webアプリ開発 実装ハンドブック では「会員制フォトギャラリー」アプリの開発の仕方について解説されており、その中で今回紹介した画像のアップロード機能の有効な活用方法を学ぶことができます。

また、画像のアップロードだけでなく、ユーザー管理(ログインなど)についてもアプリを開発しながら学ぶことができます。他にもブログアプリ問い合わせページ(メールの送信機能)の作り方などについても解説されており、Django 入門者の方が Django で出来ることの幅を広げるのに最適な書籍だと思います。

また、Django 4 向けの解説書籍であり、2022/5/30 時点だとおそらく Django 向け書籍としてはトップレベルに情報が新しいです。

書籍の難易度としては、Django 初心者の方向けというよりは、Django の入門書一冊くらいを一通り学んだことのある方向けだと思います。

「Django の入門書を一通り読んだけど次に何をすれば良いかわからない…」という方は是非読んでみてください!

同じカテゴリのページ一覧を表示

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です