【Django】herokuでCloudinaryを使い画像をアップロードする

## herokuの問題点

herokuは画像がアップロードできない仕様。

AWSなら良いのだけど、herokuの方が管理が楽で安い！

そして無料プランもある！

ってことでCloudinaryを使って実装してみる。

## herokuにアドオンを追加
herokuにログインして自分のアプリに移動する。

「Configure Add-ons」→「Find more add-ons」

command + FでCloudinaryを検索。

Image Processingの「Cloudinary - Image and Video Management」内でアドオンを追加できます。


## pip
画像を扱うにはPillowが必要なので入ってなければ入れておく。

```terminal:terminal
$ pip install Pillow
```

次にCloudinaryのパッケージを入れる。

```terminal:terminal
$ pip install django-cloudinary-storage

#動画の場合は
$ pip install django-cloudinary-storage[video]
```

requirements.txtに書き込む。

```terminal:terminal
$ pip freeze > requirements.txt
```


## Djangoに設定する
まずは`INSTALLED_APPS`に追加する。

```python:setting.py
#メディアファイルのみの場合
INSTALLED_APPS = [
    #...
    'django.contrib.staticfiles',
    'cloudinary_storage',
    'cloudinary',
    #...
]

#メディアファイル以外がある場合
INSTALLED_APPS = [
    #...
    'cloudinary_storage',
    'django.contrib.staticfiles',
    'cloudinary',
    #...
]
```
:::note info
メディアファイルのみの場合
cloudinary_storageはdjango.contrib.staticfilesより下に置く。
:::
:::note warn
メディアファイル以外の静的ファイルがある場合
django.contrib.staticfilesはcloudinary_storageより下に置く。
※ 公式サイトを参照して下さい
:::

https://github.com/klis87/django-cloudinary-storage

次にCloudinaryのシークレットキーなどを設定。

キー等はCloudinaryのダッシュボードに書いてあります。

```python:setting.py
#setting.pyの最後に追加でOK
CLOUDINARY_STORAGE  = {
    'CLOUD_NAME':'your_cloud_name',
    'API_KEY': env('CLOUDINARY_API_KEY'),
    'API_SECRET': env('CLOUDINARY_API_SECRET')
}
```
シークレットキーなど公開するべきでないものは`.env`ファイルに書いとく。

:::note warn
本番環境でのキー設定は、herokuの「config var」に入力します。
:::

あとはパスの設定

```python:setting.py
MEDIA_URL = '/media/'
DEFAULT_FILE_STORAGE = 'cloudinary_storage.storage.MediaCloudinaryStorage'
```
これで画像がアップできるようになります。


## モデルでのフィールド
ImageFieldを使った通常通りの書き方でOKです！

```python:models.py
class TestModel(models.Model):
    name = models.CharField(max_length=10)
    image = models.ImageField(upload_to="image/", blank=True)
```

## テンプレート
他同様`.image.url`でインスタンス化できます。

```html:html
<img src = "{{ test_model_instance.image.url }}">
```

ユーザーがアップロードした元のサイズで画像がダウンロードされるのでコントロールしたい場合は、

```html:html
{% load cloudinary %}

<!-- 画像を挿入したい場所に -->
{% cloudinary test_model_instance.image.name width=100 height=100 %}
```
:::note info
.image.urlでなく.image.nameなので注意。
:::

これで100 × 100のサイズが読み込まれるようになります。

さらに、絶対パスを使いたい場合は以下のようにURLを作ります。

```html:html
<img src="https://res.cloudinary.com/<your_cloud_name>/image/upload/c_fill,h_100,w_100/v1/{{ test_model_instance.image.name }}">
```
### 絶対パスの取得手順

1. 適当な画像のURLを取得
1. 画像のパスを取得
1. 絶対パスの作成
1. オプションを設定

```html:html
<!-- CloudinaryのURLの構造 -->
https://res.cloudinary.com/<cloud_name>/<asset_type>/<delivery_type>/<transformations>/<version>/<public_id>.<extension>

1,実際のURLはこんな感じかと
https://res.cloudinary.com/<your_cloud_name>/image/upload/v1/<ディレクトリ名>/hoge_svqqsy

2,画像のパスを取得
{{ test_model_instance.image.name }}で<ディレクトリ>以降が取得できます。

3,絶対パスの作成
１と２を組み合わせる。
https://res.cloudinary.com/<your_cloud_name>/image/upload/v1/{{ test_model_instance.image.name }}

4,サイズ等オプションの設定
<transformations>の場所に画像のサイズ等の設定を書きます。
https://res.cloudinary.com/<your_cloud_name>/image/upload/h_150,w_150/v1/{{ test_model_instance.image.name }}
```

### Cloudinaryの便利機能
Cloudinaryにはサイズの変更だけでなく、様々な機能があります。

顔認証などもあり、全てを書くと膨大な量になるので一部だけ紹介。

```html
/c_fill,h_150,w_150/
150×150のサイズでいい感じにフィットさせる。

/c_fill,g_face,h_150,w_150/
顔認証機能で顔にフォーカス
```
詳しく知りたい方は公式ページでご確認ください。

https://github.com/cloudinary/pycloudinary

## 使われていない画像の削除
Cloudinaryには、未使用画像の一括削除という非常に便利な機能があります。

`django-cloudinary-storage`の場合管理コマンドは次の３つのみ。

* `collectstatic`
* `deleteorphanedmedia`
* `deleteredundantstatic`

不要なメディアファイルの削除は`deleteorphanedmedia`

使えるようにするためsetting.pyに記述。

```python:setting.py
CLOUDINARY_STORAGE = {
    #その他の設定
    'EXCLUDE_DELETE_ORPHANED_MEDIA_PATHS':('path/', 'second-path/')
}
```

引数に除外するパスを書きます。

上記の場合、path/とsecond-path/のメディアファイルは削除しない設定です。

全てを対象とする場合は引数無しでOK。

## 管理コマンドの実行
本番環境で実行するため`heroku run`を最初につけます。

```terminal:terminal
$ heroku run python manage.py deleteorphanedmedia

5 files will be deleted:
- media/hoge1_g5bg8j
- media/hoge2_h8roax
- media/hoge3_uwhmgw
- media/hoge4_svqqsy
- media/hoge5_nqyljq
If you are sure to delete them, please type 'yes': 
```

yesで実行すると使われていない画像が削除されます。楽ちんだ！

## まとめると

```python:setting.py
INSTALLED_APPS = [
    #...
    'django.contrib.staticfiles',
    'cloudinary_storage',
    'cloudinary',
    #...
]

#色んな設定

MEDIA_URL = '/media/'
DEFAULT_FILE_STORAGE = 'cloudinary_storage.storage.MediaCloudinaryStorage'

CLOUDINARY_STORAGE  = {
    'CLOUD_NAME':'your_cloud_name',
    'API_KEY': env('CLOUDINARY_API_KEY'),
    'API_SECRET': env('CLOUDINARY_API_SECRET')
    'EXCLUDE_DELETE_ORPHANED_MEDIA_PATHS':(),
}
```

herokuはサーバー管理が楽なので本当に良いですね！

django-cloudinary-storageのREADME.mdで最新の情報を見れます。

https://github.com/klis87/django-cloudinary-storage