django heroku の環境で画像アップロードにcloudinaryを使う方法
djangoでherokuデプロイをする際に、画像をアップロードできる機能を実装するため、
cloudinaryを使ってみました。
ですが、ググった記事の通りにしてもうまく動作せず、詰まったためメモを残したいと思います。
cloudinaryをherokuで使う設定
herokuにログインし、resourceからcloudinaryを追加する。
cloudinaryのダッシュボードにいって、
API keyと API Secretをherokuのsettings>config varに入れる。
これでherokuの設定は完了です。
cloudinaryをdjangoで使う設定
pipfileにcloudinaryを追加します。
pipenv install cloudinary
pipenv lock -r > requirements.txt
settings.pyでcloudinaryを使えるようにします。
本番環境では、先程heokuに設定した値を読み込んでくれるように設定します。
import cloudinary
INSTALLED_APPS = [
'django.contrib.admin',
...
'django.contrib.staticfiles',
'cloudinary',
...
try:
from .local_settings import *
except ImportError:
pass
if not DEBUG:
SECRET_KEY = os.environ['SECRET_KEY']
KEY = os.environ['KEY']
ENDPOINT = os.environ['ENDPOINT']
import django_heroku
django_heroku.settings(locals())
cloudinary.config(
cloud_name='he93wwe4y',
api_key=os.environ['CLOUDINARY_API_KEY'],
api_secret=os.environ['CLOUDINARY_API_SECRET']
)
ローカル環境では、直接値を読み込むようにします。
このlocal_settingsはgitにあげないように注意してください。
cloudinary.config(
cloud_name='he93wwe4y',
api_key='xxxxxxxxxxx',
api_secret='xxxxxxxxxxx'
)
djangoで画像をアップロードする
次にmodels.pyを修正します。
今までImageField
などを使っていたのを、CloudinaryField
にします。
正直ImageFieldのままで行きたいところですが、変えても問題は特に生じないのと、
Django Cloudinary Storageを使用するほうが結構大変だったため、モデルの内容を変えることにしました。
from cloudinary.models import CloudinaryField
from django.contrib.auth.models import User
from django.db import models
class UploadedImage(models.Model):
user_im = CloudinaryField(
'image', blank=True, null=True, folder="media/face_images")
product_im = CloudinaryField(
'image', blank=True, null=True, folder="media/face_images")
image = CloudinaryField('image', blank=True, null=True,
folder="media/face_images")
author = models.ForeignKey(
User, on_delete=models.CASCADE, null=True, blank=True
)
created_at = models.DateTimeField(auto_now_add=True)
token = models.CharField(max_length=256, null=True, blank=True)
folder="media/face_images"
とはオプションになるので、必須ではありません。
保存する際にこのフォルダにアップロードされるという意味です。
他にも様々なオプションがあるので、使用してみてください。
モデルをcloudinaryfieldにすれば、あとは他にやることは殆どありません。
参考までにforms.pyとviews.pyを載せておきますが、普通にdjnagoで書くのと変わりないです。
from django import forms
class ImageForm(forms.Form):
product_im = forms.ImageField()
user_im = forms.ImageField()
def upload_image(request):
if request.method == "POST":
form = ImageForm(request.POST, request.FILES)
if form.is_valid():
user_im = request.FILES.get("user_im")
product_im = request.FILES.get("product_im")
....
uploaded_image = UploadedImage()
uploaded_image.user_im = user_im
uploaded_image.product_im = product_im
uploaded_image.image = SimpleUploadedFile(name='temp.png', content=open(
output, 'rb').read(), content_type='image/png')
uploaded_image.save()
画像を削除したい時
画像を定期実行などで削除したい場合もあると思います。
そんなときに使うのがcloudinary.uploader.destroy()
です。
引数に、パスを渡すと思って、削除されず悩んでいましたが、パスではなくpublic_idを渡します。
import datetime
import os
import sys
from datetime import timedelta
import cloudinary
import django
from django.utils.timezone import make_aware
def delete_anonymous_uploaded_image():
django.setup()
from face.models import UploadedImage
one_hour_ago = make_aware(datetime.datetime.now()) - timedelta(hours=1)
images = UploadedImage.objects.filter(
author=None, created_at__lte=one_hour_ago)
for im in images:
cloudinary.uploader.destroy(im.user_im.public_id)
cloudinary.uploader.destroy(im.product_im.public_id)
cloudinary.uploader.destroy(im.image.public_id)
im.delete()
if __name__ == '__main__':
delete_anonymous_uploaded_image()
お読みいただきありがとうございました。
参考サイト
とりあえずこの記事を読めば突破できます!
Discussion