LoginSignup
0
0

More than 1 year has passed since last update.

django heroku の環境で画像アップロードにcloudinaryを使う方法

Posted at

djangoでherokuデプロイをする際に、画像をアップロードできる機能を実装するため、
cloudinaryを使ってみました。

ですが、ググった記事の通りにしてもうまく動作せず、詰まったためメモを残したいと思います。

cloudinaryをherokuで使う設定

herokuにログインし、resourceからcloudinaryを追加する。

image.png

cloudinaryのダッシュボードにいって、
API keyと API Secretをherokuのsettings>config varに入れる。
image.png

image.png

これでherokuの設定は完了です。

cloudinaryをdjangoで使う設定

pipfileにcloudinaryを追加します。

pipenv install cloudinary
pipenv lock -r > requirements.txt

settings.pyでcloudinaryを使えるようにします。
本番環境では、先程heokuに設定した値を読み込んでくれるように設定します。

settings.py
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にあげないように注意してください。

config/local_settings.py
cloudinary.config(
    cloud_name='he93wwe4y',
    api_key='xxxxxxxxxxx',
    api_secret='xxxxxxxxxxx'
)

djangoで画像をアップロードする

次にmodels.pyを修正します。
今までImageFieldなどを使っていたのを、CloudinaryFieldにします。

正直ImageFieldのままで行きたいところですが、変えても問題は特に生じないのと、
Django Cloudinary Storageを使用するほうが結構大変だったため、モデルの内容を変えることにしました。
https://pypi.org/project/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"とはオプションになるので、必須ではありません。
保存する際にこのフォルダにアップロードされるという意味です。

他にも様々なオプションがあるので、使用してみてください。
https://cloudinary.com/documentation/image_upload_api_reference#upload

モデルをcloudinaryfieldにすれば、あとは他にやることは殆どありません。
参考までにforms.pyとviews.pyを載せておきますが、普通にdjnagoで書くのと変わりないです。

forms.py
from django import forms


class ImageForm(forms.Form):
    product_im = forms.ImageField()
    user_im = forms.ImageField()
views.py
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を渡します。

job.py
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()

お読みいただきありがとうございました。

参考サイト

とりあえずこの記事を読めば突破できます!
https://alphacoder.xyz/image-upload-with-django-and-cloudinary/
https://cloudinary.com/documentation/django_image_and_video_upload#django_forms_and_models
https://cloudinary.com/documentation/image_upload_api_reference#upload
https://dev.classmethod.jp/articles/getting-started-with-cloudinary-python-sdk/
https://github.com/cloudinary/cloudinary-django-sample
https://stackoverflow.com/questions/70514627/how-to-save-cloudinaryfield-in-a-folder-named-by-the-user/70519767

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0