Django staticファイルとAWS S3 - Qiita
Django mediaファイルとAWS S3 - Qiita
※今回の記事はDjangoを利用したブログである「Djangoブログ」(仮称)作成時に調査したことをまとめたものです。Djangoは初めてでしたが3週間程度で作りました。α版ですが、どうぞユーザ登録して使ってみてください。
「Djangブログ」 - GAEにDeployしたものです
本記事ではDjangoのmediaファイルの扱いと、AWS S3へのアップロードについてまとめます。
DjangoではFileField、ImageFieldでアップロードされるファイルをmediaファイルと呼びます。
#1.localへのアップロード - 開発環境(DEBUG=True)
##1-1.テストプロジェクト作成
python -m venv localupload
source localupload/bin/activate
cd localupload/
pip freeze
pip install django
django-admin startproject localupload .
Remoteからアクセスするための設定です。
---
ALLOWED_HOSTS = ["www.mypress.jp"]
---
ここまででプロジェクトが動作するので確認します。
python manage.py migrate
python manage.py runserver 0:8080
#1-2.localへのアップロード
localへのアップロードするアプリケーションを作成します。
python manage.py startapp uploadapp
---
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'uploadapp',
]
---
---
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
---
- MEDIA_URL : メディアファイル公開時のURLのプレフィクス。url=http://アプリのドメイン+MEDIA_URL+メディアファイル名
- MEDIA_ROOT : サーバから見たメディアルートの絶対パス. プロジェクトトップディレクトリ/media
from django.conf import settings
from django.contrib import admin
from django.urls import path, include
from django.conf.urls.static import static
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('uploadapp.urls')),
]
if settings.DEBUG:
urlpatterns += static(settings.MEDIA_URL, document_root=settings.MEDIA_ROOT)
開発中(DEBUG=True)の時は最後のスニペットを追加することで、画像はlocalなStorageのMEDIA_ROOTに保存され、localなurl(相対url)のMEDIA_URLで参照できます。 しかし本番環境(DEBUG=False)ではこのやり方は推奨されていません。staticファイルの場合と同じです。
静的ファイル (画像、JavaScript、CSS など) を管理する - 公式ドキュメント
Django staticファイルとAWS S3 - Qiita
from django.urls import path
from . import views
urlpatterns = [
path('', views.Photo.as_view()),
]
さて今回は、FileFieldでなくImageFieldを使います。ImageFieldは次のようなものです。
- ImageFieldは画像を扱うことに特化したFileFieldの派生フィールドです
- ImageFieldにはPillowが必要。
- 登録時のバリデーションで画像ファイルのチェックをする。画像ファイルでないもの、データの破損があるものは登録エラーとする(専用エラーメッセージあり)。
- 管理用のフィールドを事前に用意すると、登録時に画像の高さと幅(pixel単位)を取得して保存する
それではPillowをインストールします。
pip install Pillow
ImageFieldを使ってmodelsを定義します。
from django.db import models
class PhotoModel(models.Model):
image = models.ImageField(upload_to='images')
def __str__(self):
return self.image.url
formsを定義します。
from django import forms
from .models import PhotoModel
class PhotoForm(forms.ModelForm):
class Meta:
model = PhotoModel
fields = '__all__'
viewsを定義します。
from django.views import generic
from .forms import PhotoForm
from .models import PhotoModel
class Photo(generic.CreateView):
model = PhotoModel
form_class = PhotoForm
template_name = 'uploadapp/upload.html'
success_url = '/'
def get_context_data(self, **kwargs):
context = super(Photo, self).get_context_data(**kwargs) # はじめに継承元のメソッドを呼び出す
context["photos"] = PhotoModel.objects.all()
return context
テンプレートのフォルダを作成します。
mkdir -p uploadapp/templates/uploadapp/
アップロードフォームとアップロード済みの画像リスト表示です。
<form action="" method="POST" enctype="multipart/form-data">{% csrf_token %} {{ form.as_p }}
<button type="submit">アップロード</button>
</form>
{% for photo in photos %}
<a href="{{ photo.image.url }}">{{ photo }}</a>
<hr>
{% endfor %}
プロジェクトを起動します。
python manage.py runserver 0:8080
2つの画像をアップロード済みの画面です。
MEDIA_URL の設定を考慮して、以下のHTMLソースを確認してください。photo.image.url="/media/images/tree5.jpg" のように展開されています。
<form action="" method="POST" enctype="multipart/form-data"><input type="hidden" name="csrfmiddlewaretoken" value="m0qzyYvLhuUckbF5ctHAAJOCThqZ24Q7RE5c70EQ4bnTm05v3F33aYrn7br4jed2">
<p><label for="id_image">Image:</label> <input type="file" name="image" accept="image/*" required id="id_image"></p>
<button type="submit">アップロード</button>
</form>
<a href="/media/images/tree4jpeg.jpeg">/media/images/tree4jpeg.jpeg</a>
<hr>
<a href="/media/images/tree5.jpg">/media/images/tree5.jpg</a>
<hr>
MEDIA_ROOTの設定を確認してください。プロジェクトのトップディレクトリでtreeコマンドを発行した結果です。
$ tree media/
media/
├── images
│ ├── tree4jpeg.jpeg
│ └── tree5.jpg
└── tree4jpeg.jpeg
#2.Remote の AWS S3 へアップロード - 本番環境(DEBUG=False)
ここから、画像をAWS S3にアップロードする設定を見ていきます。本番環境なので、基本はstatcファイルもAWS S3にdeployする設定を行い、追加でmediaファイルもAWS S3にアップロードする設定を行います。
staticファイルとAWSの設定については「Django staticファイルとAWS S3 -Qiita」を参照してください。
まずは本番環境の設定です。
---
DEBUG = False
---
AWS S3に新バケットを作り、以下のポリシーを付与します。
{
"Version":"2012-10-17",
"Statement":[
{
"Sid":"AddPerm",
"Effect":"Allow",
"Principal": "*",
"Action":["s3:GetObject"],
"Resource":["arn:aws:s3:::media-test-reiwa/*"]
}
]
}
必要なライブラリをインストールします。
pip install boto3
pip install django-storages
ライブラリを有効にします。
---
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
'uploadapp',
'storages', # 追加
]
---
# STATIC_URL = '/static/'
STATICFILES_DIRS = [os.path.join(BASE_DIR, 'static')]
AWS_ACCESS_KEY_ID = 'AKIAXXXXXXXXXXXXXXXXXXXXX'
AWS_SECRET_ACCESS_KEY = 'YYYYYYYYYYYYYYYYYYYYYY'
AWS_STORAGE_BUCKET_NAME = 'media-test-reiwa'
AWS_S3_CUSTOM_DOMAIN = '%s.s3.amazonaws.com' % AWS_STORAGE_BUCKET_NAME
AWS_S3_OBJECT_PARAMETERS = {
'CacheControl': 'max-age=86400',
}
AWS_LOCATION = 'static'
AWS_DEFAULT_ACL = None
STATIC_URL = 'https://%s/%s/' % (AWS_S3_CUSTOM_DOMAIN, AWS_LOCATION)
STATICFILES_STORAGE = 'storages.backends.s3boto3.S3Boto3Storage'
MEDIA_URL = '/media/'
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
DEFAULT_FILE_STORAGE = 'localupload.storage_backends.MediaStorage'
staticファイルの時の設定に加えて、DEFAULT_FILE_STORAGEの設定を追加しています。
(説明) static assets と media assets
- static ファイルはSTATICFILES_STORAGE=S3Boto3Storageで設定。collectstaticが処理する。
- media ファイルはDEFAULT_FILE_STORAGEで設定。S3Boto3Storageを拡張し、'media'ディレクトリにアップロードするよう指定する。
S3Boto3Storageの拡張はDEFAULT_FILE_STORAGEで指定したように、以下のstorage_backends.pyで行います。同一ファイル名の画像がアップロードされたら上書きせずに適当にリネームするように設定しています。
from storages.backends.s3boto3 import S3Boto3Storage
# 画像は同ファイル名での上書きを許さない
class MediaStorage(S3Boto3Storage):
location = 'media'
file_overwrite = False
staticディレクトリを作成し、staticファイルをcollectstaticでAWS S3にdeployします。
mkdir static
python manage.py collectstatic
ここでDBテーブルから前にアップロードしたエントリーを全削除しておきます。
プロジェクトを起動し、画像ファイルを2個アップロードします。
python manage.py runserver 0:8080
以下のような画面が確認できます。
AWS S3のバケットにはstaticとmediaディレクトリできています。
mediaディレクトリには確かに2ファイルがアップロードされています。
今回は以上です。