DjangoでCloudinaryを使いたい！！

## はじめに
私は2022年の4月から一念発起して、エンジニアを目指し勉強を開始したtoamokuです。
オンラインスクールとUdemyなどを活用し、日々学習しております。転職活動も同時に行っておりますが、まだ決まっておりません。早くエンジニアになりたーい！

今回はQiitaに初めて記事を投稿することにしました。今までは全く外に向けての発信などはしていなかったので、学習内容の復習と言語化をするためにこの記事を書いています（転職活動の一環でもあります）。ぜひ最後までご覧いただけると幸いです。

## 前提条件
Djangoについては、基本的な知識があることを前提でお話を進めてまいります。
PCはmacを使用しています。

## Djangoの紹介
Djangoは、Pythonで開発された高水準のWebフレームワークです。再利用可能なコードの提供、シンプルなURL設計、強力な管理インターフェース、そしてセキュリティ機能など、豊富な機能を備えています。

これにより、開発者は煩雑なWeb開発タスクを自動化し、アプリケーションの構築に集中できるようになります。また、Djangoはオープンソースであり、活発なコミュニティによって継続的に改良されています。

そのため、初心者からプロフェッショナルまで幅広い開発者に利用されています。
詳細はMDNのドキュメントをご覧ください。

https://developer.mozilla.org/ja/docs/Learn/Server-side/Django/Introduction

## Cloudinaryの紹介
Cloudinaryは、画像や動画の管理、最適化、および配信を容易にするクラウドベースのメディア管理サービスです。

開発者はCloudinaryを利用して、メディアファイルのアップロード、保存、変換、編集、そして配信を一元的に管理できます。

Cloudinaryは、Webおよびモバイルアプリケーションのパフォーマンス向上とユーザーエクスペリエンスの最適化に貢献するため、幅広い業界で利用されています。

## 実現したいこと
1. 画像をCloudinaryにアップロードする
1. アップロードした画像のURLをデータベースに保存する
1. 画像はURLから取得して表示する

データベースはデフォルトのSQLiteを使用しています。
このコードはCloudinaryとの連携のみに重点を置いておりますので、見た目は全く気にしておりません。予めご了承ください。

## 環境設定
PCにはPythonがインストールされていることを前提として、お話を進めていきたいと思います。
インストールがお済みでない方は下記の公式ドキュメントをご覧ください。
 
 https://docs.python.org/ja/3/using/index.html
 
またググると他にも色々記事がございますので、併せてご覧ください。

:::note info
仮想環境の有効化も忘れないでくださいね。
:::
 
 ```bash
 $ pip install django cloudinary django-cloudinary-storage　python-dotenv
```
python-dotenvは.envファイルを読み込むためにインストールします。

## Djangoのプロジェクトとアプリの作成
myprojectディレクトリを作成します。
 ```bash
 $ django-admin startproject myproject
 ```
 続いてDjangoのプロジェクト内で新しいアプリケーションcloudinaryappを作成します。
 ```bash
 $ cd myproject
 $ python manage.py startapp cloudinaryapp
```

## myprojectの設定
settings.pyファイルを開き、以下の設定を追加します。

なおsettings.pyにはSECRET_KEYが設定されているので、環境変数として.envファイルに記載するのが推奨されます。ローカル環境のみでの使用であれば、そのまま利用しても構いません。

```python:settings.py
# settings.py

import os
from pathlib import Path
from dotenv import load_dotenv

load_dotenv() # .envファイルを読み込む

SECRET_KEY = os.getenv('SECRET_KEY')　# SECRET_KEYを.envファイルから呼び出す

INSTALLED_APPS = [
    # その他のアプリ
    'cloudinaryapp'
    'cloudinary',
    'cloudinary_storage',
]

CLOUDINARY_STORAGE = {
    'CLOUD_NAME': os.getenv('CLOUDINARY_CLOUD_NAME'), # cloudinaryのCLOUD_NAME
    'API_KEY': os.getenv('CLOUDINARY_API_KEY'), # cloudinaryのAPI_KEY
    'API_SECRET': os.getenv('CLOUDINARY_API_SECRET') # cloudinaryのAPI_SECRET
}

DEFAULT_FILE_STORAGE = 'cloudinary_storage.storage.MediaCloudinaryStorage'

# Static files (CSS, JavaScript, Images)
STATIC_URL = '/static/'
MEDIA_URL = '/media/'
```

## モデルの作成
次に、モデルを作成します。ここでは画像ファイルを保存するモデルを定義します。
```python:models.py
# models.py

from django.db import models
from cloudinary.models import CloudinaryField

class Photo(models.Model):
    title = models.CharField(max_length=100)
    image = CloudinaryField('image')

    def __str__(self):
        return self.title
```
#### CloudinaryField とは
CloudinaryField は、Cloudinaryライブラリの一部で、Djangoモデルのフィールドとして使用されます。
#### このフィールドの役割
CloudinaryField を使用することで、Djangoモデルのインスタンスに関連付けられた画像を直接Cloudinaryに保存することができます。これにより、ローカルサーバーではなく、クラウド上に画像が保存され、管理や配信が容易になります。

## 管理者サイトの設定
モデルをDjangoの管理者サイトで管理できるようにします。
```python:admin.py
# admin.py

from django.contrib import admin
from .models import Photo

admin.site.register(Photo)
```

## 管理者アカウントの作成
管理者アカウントを作成するために、以下のコマンドを実行します。
```bash
$ python manage.py createsuperuser
```
これにより、ユーザー名、メールアドレス、パスワードを入力するプロンプトが表示されます。必要な情報を入力して、管理者アカウントを作成してください。

## フォームの作成
画像ファイルをアップロードするためのフォームを作成します。
```python:forms.py
# forms.py

from django import forms
from .models import Photo

class PhotoForm(forms.ModelForm):
    class Meta:
        model = Photo
        fields = ['title', 'image']
```

## ビューの作成
画像のアップロードと表示を行うビューを作成します。
```python:views.py
# views.py

from django.shortcuts import render, redirect
from .forms import PhotoForm
from .models import Photo

def upload_photo(request):
    if request.method == 'POST':
        form = PhotoForm(request.POST, request.FILES)
        if form.is_valid():
            form.save()
            return redirect('photo_list')
    else:
        form = PhotoForm()
    return render(request, 'cloudinaryapp/upload_photo.html', {'form': form})

def photo_list(request):
    photos = Photo.objects.all()
    return render(request, 'cloudinaryapp/photo_list.html', {'photos': photos})
```

## URLパターンの指定
URLパターンを追加して、ビューにアクセスできるようにします。
myproject/urls.pyとcloudinaryapp/urls.pyを編集します。

```python:urls.py
# myproject/urls.py

# admin以外はcloudinaryappのurls.pyでルーティングを行う
from django.contrib import admin
from django.urls import path, include

urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include('cloudinaryapp.urls'))
]


# coudinaryapp/urls.py

from django.urls import path
from . import views

urlpatterns = [
    path('', views.photo_list, name='photo_list'),
    path('upload/', views.upload_photo, name='upload_photo'),
]
```

## テンプレートディレクトリの作成とsettings.pyの設定
画像のアップロードフォームと画像リストを表示するテンプレートを作成します。
最初にテンプレートディレクトリをmyprojectと同じ階層に作成します

```bash:
$ mkdir -p templates/cloudinaryapp
```
続いてsettings.pyを編集します。
```python:settings.py
TEMPLATES = [
    {
        'BACKEND': 'django.template.backends.django.DjangoTemplates',
        # os
        'DIRS': [os.path.join(BASE_DIR, 'templates')],
        # Path
        'DIRS': [BASE_DIR / 'templates'], # 個々の部分を編集する
        'APP_DIRS': True,
        'OPTIONS': {
            'context_processors': [
                'django.template.context_processors.debug',
                'django.template.context_processors.request',
                'django.contrib.auth.context_processors.auth',
                'django.contrib.messages.context_processors.messages',
            ],
        },
    },
]
```
## テンプレートファイルの作成
先ほど作成した templates/cloudinaryapp ディレクトリにテンプレートファイルを配置します。
アップロード用ファイルを作成します。
```html:upload_photo.html
<!-- templates/cloudinaryapp/upload_photo.html -->

<!DOCTYPE html>
<html>
<head>
    <title>Upload Photo</title>
</head>
<body>
    <h1>Upload Photo</h1>
    <form method="post" enctype="multipart/form-data">
        {% csrf_token %}
        {{ form.as_p }}
        <button type="submit">Upload</button>
    </form>
    <a href="{% url 'photo_list' %}">View Photos</a>
</body>
</html>
```

続いてアップロードした画像のリストを表示するファイルを作成します。

```html:photo_list.html
<!-- templates/cloudinaryapp/photo_list.html -->

<!DOCTYPE html>
<html>
<head>
    <title>Photo List</title>
</head>
<body>
    <h1>Photo List</h1>
    <ul>
        {% for photo in photos %}
            <li>
                <h2>{{ photo.title }}</h2>
                <img src="{{ photo.image.url }}" alt="{{ photo.title }}" width="300">
            </li>
        {% endfor %}
    </ul>
    <a href="{% url 'upload_photo' %}">Upload New Photo</a>
</body>
</html>
```

## マイグレーションの実行
先にmakemigrationsを実行しデータベースの構造やデータの変更記録を残します。そのあとにmigrateで変更結果をデータベースに反映させます。
```bash
$ python manage.py makemigrations
$ python manage.py migrate
```

## 完成したディレクトリ構造
最終的なディレクトリ構造はこのようになっていると思います。
```bash
myproject/
├── cloudinaryapp/
│   ├── admin.py
│   ├── apps.py
│   ├── forms.py
│   ├── models.py
│   ├── tests.py
│   ├── urls.py
│   ├── views.py
│   └── ...
├── templates/
│   └── cloudinaryapp/
│       ├── upload_photo.html
│       └── photo_list.html
├── manage.py
└── myproject/
    ├── settings.py
    ├── urls.py
    └── ...
```

## 補足
GitHubなどを使用して、コードを管理する場合はrequirements.txtを作成します。
requirements.txtは現在の仮想環境にインストールされているすべてのパッケージとそのバージョンを記録しておくファイルになります。
 ```bash
 $ pip freeze > requirements.txt
 ```

:::note warn
 外部に公開しない情報は、.envファイルを使用します。
　.env ファイルは、環境変数を管理するために使用されます。環境変数は、アプリケーションの設定や機密情報（APIキーやデータベースの資格情報など）をコードから分離するために利用されます。以下は、.env ファイルで管理される代表的なものです。

- APIキー
- データベース接続情報
- シークレットキー
- メールサーバー設定
- サードパーティサービスの資格情報（AWS、GCPなど）
 
　Djangoプロジェクトを作成した場合、settings.pyにSECRET_KEYがあります。これも.envファイルの格納するようにしましょう。下は.envファイルにcloudinary用の環境変数を設定したサンプルになります。
```python:.env
# 下記はすべてサンプルキーです
CLOUDINARY_CLOUD_NAME=kshfe3r9a
CLOUDINARY_API_KEY=1561613515
CLOUDINARY_API_SECRET=kji8_alo3jt_ok94

SECRET_KEY=<settings.pyに入力されていた文字列を記載>
```
:::

## 最後に
最後までご覧いただき、ありがとうございます。この記事が少しでも皆さんのお役に立てれば幸いです。
これからも継続的に記事を掲載していきたいと思いますので、よろしくお願いします。