Googleが提供するGAE(Google App Engine)を用いてDjangoアプリケーションをホスティングした場合、ImageFieldを使用した画像を表示させようとするとうまくいきません。
「GAE(GCP)で画像も扱いたい!」
そんな方向けに、解決策として Google Cloud Storage を用いて画像を使用する方法をご紹介します。
・なぜそのままだと画像を扱えないか
・サービスアカウントの作成、連携
・バケットの作成、連携
なぜ画像を扱えないのか
簡潔に言うと、「CloudSQL(MySQL)がメディアファイルのアップロードに対応していないから」です。
なので、メディアファイルを別の場所にアップロード・管理し、アプリケーションと紐づけて管理する必要があります。
この「別の場所」として Google Cloud Storage(以下 Cloud Storage) を使用します。
さらにこの Cloud Storage とDjangoアプリケーションを紐づけることで、メディアファイルを扱うことができるようになります。
ちなみにモデルにて ImageField として定義している項目については、データベース上でファイル名が保存されます。
例えば、モデルでこのように項目を定義しているとします。
from accounts.models import CustomUser
from django.db import models
class CafeMenu(models.Model):
photo = models.ImageField(verbose_name='写真', blank=True, null=True)
name = models.CharField(verbose_name='商品名', max_length=255, default='商品名')
price = models.PositiveIntegerField(verbose_name='値段')
description = models.CharField(verbose_name='説明', max_length=255)
# ... 略 ...
「photo」が ImageField です。さらに実際のフォームでこのように入力してレコードを作成したとしましょう。
するとデータベースにはこのような形で保存されます。
ご覧の通り、入力フォームではメディアファイルをアップロードしていますが、データベースではそのファイルパスが保存されています。このファイルパスを利用して Cloud Storage から画像を引っ張り出して、アプリケーション上で表示させます。
それでは実際に実装していきましょう!
実装する
Cloud Storage を使用してメディアファイルを使用できるようになるまでの流れはこの通りです。
- Google Cloud のサービスアカウントを作成
- Cloud Storage(バケット) を作成する
- サービスアカウントとバケットを紐づける
- Djangoの設定ファイル settings.py を編集
GCPに馴染みがない方はこの時点ではイメージがつかないかと思いますが、コンソール上で簡単にできるので安心してください!
Google Cloud のサービスアカウントを作成
まずはサービスアカウントと呼ばれるものを作成します。
上記のリンクにアクセスします。
GCPのプロジェクト選択画面に移動します。Djangoアプリケーションをデプロイしているプロジェクトを選択しましょう。
選択したプロジェクトのサービスアカウント一覧ページに移動します。画面上部の「+ サービスアカウントを作成」をクリックします。
作成するサービスアカウントの詳細入力ページに移動します。任意のサービスアカウント名を入力し、「作成して続行」をクリックします。
作成したサービスアカウントが「何にアクセスできて、どんな事までできるのか」を設定します。「ロールを選択」をクリックします。
ロールに「storage」と入力→必要に応じた「Storage オブジェクト」を選択。GCSオブジェクトの作成・読み取り・削除も行う前提で「管理者」としています。
最後に「完了」をクリックしてサービスアカウントを作成します。
サービスアカウント一覧ページに移動します。
先ほど作成したサービスアカウントの右にある「・」が縦に3つ並んだやつ(語彙力)→「鍵を管理」の順でクリックしましょう。
「鍵を追加」→「新しい鍵を作成」の順でクリックします。
「キーのタイプ」はJSONを選択し、「作成」ボタンを押しましょう。
するとJSONデータがローカルマシンにダウンロードされます。そのJSONデータを、Djangoアプリケーションのルートディレクトリ(manage.pyがあるとこ)に移動しておきましょう!
Cloud Storage(バケット) を作成する
続いてメディアファイルを含めた静的ファイル(CSS、jsなど)を格納しておく Cloud Storage にバケットを作成します。
バケットは Cloud Storage内のコンテナ単位のことを指します。
上記のリンクにアクセスします。
プロジェクトのバケット一覧ページに遷移します。上部の「作成」ボタンをクリックしましょう。
バケット設定画面に移動します。バケット名は自分勝手に命名することはできず、いくつかルールがあるので注意です。
リージョンは「asia-northeast1(東京)」としました(お好きなところで大丈夫です)。
ストレージクラスも、用途に合わせて選択してください。
アクセス制御の項目では「公開アクセス禁止」のチェックを外しましょう。
アプリケーションからアクセスできなくなってしまいます。
データ保護の設定はデフォルト通り「なし」にしています。
ここまで設定が完了したら、画面下部の「作成」ボタンをクリックしてバケットを作成しましょう!
サービスアカウントとバケットを紐づける
残念ながら、バケットを作成しただけではアプリケーションからアクセスすることはできません。
そういえば、記事の最初の方でサービスアカウントを作成しましたね。Storageへのアクセス権限も付与していたので大丈夫そうですが、サービスアカウントを作成しただけではまだアクセスすることはできません。
バケット側でも「このアカウントならアクセスしてええで」という設定をしてあげる必要があります。
すなわち「バケットへのアクセス権を持った特定のサービスアカウントを許可する設定」を行う必要があります。
それでは実際にこの作業を行なっていきます。
最初に、先ほど作成したサービスアカウントのメールアドレスを控えておきます。
こちらにアクセスしましょう。
サービスアカウント一覧ページに移動します。
この中から、先ほど作成したStorageへのアクセス権限を付与したサービスアカウントのメールアドレスをコピーしておきましょう。
このリンク先にアクセスします。
バケット一覧ページに遷移します。この中から、先ほど作成したバケット名をクリックします。
バケットのトップページに遷移するかと思います。「権限」タブ→「アクセス権を付与」の順でクリックしましょう。
このようにプリンシパルとロールの設定画面が表示されます。それぞれ上図のように入力し、最後に「保存」をクリックして保存しましょう。
以上でバケット側の許可設定はおしまいです!
Djangoの設定ファイル settings.py を編集
やっとDjango側での設定です。
Django側に先ほど作成したサービスアカウントを携えることで、バケットにアクセスすることが出来ます。
まず必要なライブラリ群をインストールしましょう。
(venv)$ pip install django-storages
(venv)$ pip install google-cloud-storage
忘れずに requirements.txt を更新しましょう。
(venv)$ pip freeze > requirements.txt
続いて settings.py に下記のように記述をします。
import os
from google.oauth2 import service_account
GS_CREDENTIALS = service_account.Credentials.from_service_account_file(
os.path.join(BASE_DIR, '<サービスアカウントの認証鍵のファイル名>.json')
)
DEFAULT_FILE_STORAGE = 'storages.backends.gcloud.GoogleCloudStorage'
STATICFILES_STORAGE = 'storages.backends.gcloud.GoogleCloudStorage'
GS_BUCKET_NAME = '<バケット名>'
STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
MEDIA_URL = 'https://storage.googleapis.com/<バケット名>/'
<サービスアカウントの認証鍵のファイル名>は、先ほど作成したJSONファイルの名前をコピペしましょう。
<バケット名>も先ほど作成したバケット名をそのままぶち込んでください。
これで settings.py の設定は終了です!
動作確認
きちんと動作するか確認します。
本番環境では開発環境と異なり、静的ファイルを1つの場所にまとめておく必要があります。
特段難しいことを行う必要はなく、下記のコマンドを行えば一発でできます。
(venv)$ python manage.py collectstatic
これを行うと、作成したバケットに静的ファイルがアップロードされるはずです。
中身はもちろんプロジェクトによって異なります。
あとはいつも通りデプロイすればOKです!お疲れ様でした!
まとめ
本記事では、GAEにデプロイしたDjangoアプリケーションでメディアファイルを正常に扱う方法をご紹介しました。
しかし、現段階ですと正常に画像は表示されるのですが、アプリケーション側でレコード(データベース)を削除してもバケットにはメディアファイルが残ったままです。
こちらの記事で解決方法をご紹介しておりますのでぜひご覧ください。
ご覧いただきありがとうございました!
詳しく気になる部分や間違い等ございましたら是非お声がけください。
コメント
コメント一覧 (1件)
[…] 【Django×GoogleCloudStorage】メディアファイルを本番環境で扱う Googleが提供するGAE(Google App […]