【Django】画像ファイルをアップロード・表示(配信)する方法(モデル利用)

2021.12.29 /

【Django】画像ファイルをアップロード・表示(配信)する方法(モデル利用)

本記事ではPythonのフレームワークDjangoで、画像ファイルアップロード表示する方法について解説していきます。

ここではモデルを利用するため、ImageFieldをフィールドとして持たせます。

アップロードされた画像ファイルを表示(配信)する場合は、本記事で紹介している仕組みを利用します。

モデルを利用せずに画像をサーバーにアップロードする方法については以下記事をご参照ください。

Pillowのインストール

モデルでImageFieldを使用する場合は、画像処理ライブラリであるPillowをインストールする必要があります。

PillowはPIL(Python Image Library)からフォークされたライブラリであり、Pythonで画像処理を行う際に利用されます。

Pillowのインストールはpipコマンドを利用します。

pip install pillow

mediaフォルダの作成

画像などのファイルを保存するためのフォルダを作成します。

ここではベースフォルダ(manage.pyが置いてあるフォルダ)にmediaフォルダを作成してください。

settings.py:メディア設定追加

画像などのファイルをアップロードおよび表示(配信)するためには、以下に示す条件を満たさなければなりません。

  1. アップロード先のフォルダを指定
  2. アップロードしたファイルを表示(公開)するためのパスを指定

上記条件を満たすためにsettings.pyに以下2つの設定を追加します。

設定値 説明
MEDIA_URL ファイルへのURLを指定
MEDIA_ROOT ファイルのアップロード先フォルダを指定

MEDIA_URLはデフォルトで「/media/」が設定されています。基本的にはデフォルトのままでいいです。クライアント側がファイルにアクセスするためのURLを指定しています。
例えばoffice54.pngにアクセスする場合は以下のようなURLになります。

MEDIA_ROOTは開発環境(ローカル環境)と本番環境で設定値が異なります。

開発環境(ローカル環境)では次のようにMEDIA_ROOTを指定します。

MEDIA_ROOT = BASE_DIR / 'media'

本番環境(リバースプロキシで配信)では以下のように設定します。

MEDIA_ROOT = f'/var/www/{BASE_DIR.name}/media'

実際にsettings.pyに書き込むときは、開発環境でも本番環境でも動くように次のようにします。

MEDIA_URL = '/media/'
if DEBUG:
    MEDIA_ROOT = BASE_DIR / 'media'
else:
    MEDIA_ROOT = f'/var/www/{BASE_DIR.name}/media'

開発環境では「DEBUG = True」、本番環境では「DEBUG = False」になるため上記のようになります。

urls.py:pathの追加

プロジェクト内のurls.pyへ以下を追加します。

from django.conf import settings
from django.conf.urls.static import static

urlpatterns = [
    …
]
# 以下を追加する
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)

models.py:ImageFieldを定義

モデルに画像を保存するためのImageFieldを定義します。

以下では例として、モデルUserにプロフィール画像を保存するフィールドprofile_imageを定義しています。

class User(AbstractBaseUser, PermissionsMixin):
    …
    profile_image = models.ImageField(null=True, blank=True)

ImageFieldのオプション「upload_to」を利用すると、アップロード先のフォルダを動的に変更できます。

新たにフィールドを定義したら、忘れずにマイグレーションファイルの作成およびmigrateを実行してください。

python manage.py makemigrations
python manage.py migrate

forms.py:フォームクラスの作成

画像をアップロードするためのフォームを作成していきます。

Djangoにはフォームを簡単に作成する仕組みが備わっています。Djangoでフォームを利用する方法は以下記事をご参照ください。

ここではモデルを利用したフォームを作成します。詳しい方法が知りたい方は以下記事をご参照ください。

以下例のようにフォームクラスではModelFormを継承して作成します。

class UserInfoEdit(forms.ModelForm):
    class Meta:
        model = User
        fields = ('email','username','profile_image')

views.py:アップロード時の処理を追記

views.pyに画像がアップロードされたときの処理を以下のように追加します。
以下例ではアカウント情報(プロフィール画像を含む)の変更フォームが送信されたときの処理を記しています。

def account_edit(request, account_id):
    account = get_object_or_404(User, pk=account_id)
    if request.method == "POST":
        form = UserInfoEdit(request.POST, request.FILES, instance=account)
        if form.is_valid():
            form.save()
            messages.add_message(request, messages.SUCCESS, "SUCCESS")
            return redirect('account_edit', account_id=account_id)
        messages.add_message(request, messages.ERROR, "FAIL")
    else:
        form = UserInfoEdit(instance=account)
    return render(request, 'accounts/account_edit.html', {'form':form})

ここで重要なのが、フォームクラスの引数にrequest.FILESを指定しておくことです。

またフォームクラスの引数によって、データベースへの新規登録なのか更新登録なのかが変わります。詳しくは以下記事をご参照ください。

今回のように情報を更新するような処理の場合、処理が成功したかどうかをユーザーに伝えるのがよいシステムと言えます。

そこで上記例でも利用していますが、通知メッセージ機能(フラッシュメッセージ)を使うことをお勧めします。

Djangoに通知メッセージ機能を追加する方法は以下記事をご参照ください。

テンプレート:フォームタグの利用

テンプレートで作成したフォームクラスを利用するためにフォームタグを使用します。
以下例はbootstrap4を利用しています。

<form class="" method="post" enctype="multipart/form-data">
    {% csrf_token %}
    {% bootstrap_form form %}
    {% bootstrap_button button_type="submit" content="Edit" %}
</form>
注意点

画像ファイルをformで扱えるように「enctype="multipart/form-data"」を必ず記載しましょう

このようにコードを書くことで、Djangoでは簡単に下図のようなフォームが簡単に作成できます。

Django:プロフィール編集ページ

テンプレート:画像の表示

アップロードされた画像を表示する実装例を以下に記します。
ここではユーザーアカウントのプロフィール画像が存在する場合に画像を表示するコードになります。

{% if user.profile_image %}
<li class="nav-item"><img src="{{ user.profile_image.url }}" alt=""></li>
{% endif %}

プロフィール画像が存在する場合に、「user.profile_image」に対して「url」属性で画像が配信されるURLが取得できます。

まとめ

本記事「【Django】画像ファイルをアップロード・表示(配信)する方法(モデル利用)」はいかがでしたか。

Djangoにアップロード機能は簡単に追加できます。本記事を参考にして、Webアプリケーションにアップロード機能を実装してみてください。