【初心者】DjangoをHerokuへデプロイする工程

# はじめに
どうも、かけ出す前エンジニアのYuyaです。

今回はGitHubにpushしたECサイトの商品一覧、詳細画面機能を
Herokuを使ってデプロイする工程を解説します。

以下のチュートリアルを参考にしながらやりました。

:::note warn
あくまで自分のアウトプット用です。
順番が違っていたり雑な点があるかと思いますがご了承ください。
:::

https://devcenter.heroku.com/articles/getting-started-with-python

# 前提条件
今後記載することは以下の環境下でのみ行っています。ご了承ください。
- macOS Sonoma バージョン14.6
- Python 3.13.1
- Gitをインストール済み　→　```brew install git```
- 仮想環境を作成済み　→　```python3 -m venv venv```
- `feature/products`というブランチを`main`ブランチから切って作業
- pull request のブランチをデプロイして動作確認ができる状態にすることがゴール

# ディレクトリ構成
```
django_ec/
├── Dockerfile
├── docker-compose.yml
├── manage.py
├── venv                   # 仮想環境を構築（pipを多用します）
├── .env
├── config/                # Djangoプロジェクトのディレクトリ
│   ├── __init__.py
│   ├── asgi.py
│   ├── settings.py
│   ├── urls.py
│   └── wsgi.py
└── products/                    # Djangoアプリケーションのディレクトリ
    ├── __init__.py
    ├── admin.py
    ├── apps.py
    ├── migrations/
    │   └── __init__.py
    ├── models.py
    ├── tests.py
    ├── urls.py
    └── views.py
└── requirements.txt   
```
:::note info
- 今回よく編集するファイルは`config/settings.py`です
- Herokuに何か操作する場合は`heroku`コマンドが入る
:::
# Herokuのアカウントを作成
https://jp.heroku.com/

こちらでHerokuのアカウントを作成しましょう。
クレジットカード情報の登録が必須になります。
# 基礎設定
## Herokuをインストール
自分のPCにHerokuをインストールしていきます。
```
brew tap heroku/brew && brew install heroku
```
## Herokuへログイン
以下のコマンドを実行しましょう。
```
heroku login
```
アカウント作成が正常に完了していれば以下の出力結果が返ってきます。キーボードのどこでもいいのでどこかタイプしましょう（Enterとかeとか何でもいい）
```
heroku: Press any key to open up the browser to login or q to exit:
```
どこかタイプすると勝手にブラウザが立ち上がり以下の画像になるので、Log inをクリックしましょう
![スクリーンショット 2025-01-21 23.27.14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3918502/5d65214d-2f2b-feb4-d9e4-0c81daa13fd8.png)

以下の画像になればLog in成功です！

![スクリーンショット 2025-01-21 23.28.44.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3918502/ebcba2a4-217b-899b-2107-96edf1ba343f.png)

# Procfileを定義
次にプロジェクトファイルと同階層に`Procfile`を作成しましょう
```
django_ec/
├── ...
├── manage.py
├── Procfile       # 新規作成(.pyとかいらない）
├── .env
├── config/  
    ├── ...
```
`Procfile`を使用することで
アプリを起動するために実行するコマンドを明示的に宣言します。
```:Procfile
web: gunicorn config.wsgi --log-file -
release: ./manage.py migrate --no-input
```
- `web`プロセスはHerokuでアプリケーションサーバーを起動し
リクエストを処理するために必要
- `release`プロセスはデプロイ後のセットアップ
（ここではデータベースのマイグレーション）を自動化し
アプリケーションを正常に稼働させるために必要です

# アプリをデプロイする
## アプリを作成
Herokuがソースコードを受け取れるようにアプリを作成します。
```
heroku create
Creating app... done, ⬢ serene-caverns-82714
https://serene-caverns-82714.herokuapp.com/ | https://git.heroku.com/serene-caverns-82714.git
```
アプリを作成すると、Git リモートherokuも作成されてローカル Git リポジトリに関連付けられます。 

Git リモートは、他のサーバー上にあるリポジトリのバージョンです。アプリをデプロイするには、アプリに関連付けられた Heroku でホストされている特別なリモートにコードをプッシュします。

:::note warn
アプリ名を決めてやる方法もありますが、ここでは割愛します
:::
## 変更を`commit`してデプロイ
修正したコードを以下のコマンドでコードをデプロイしましょう。

:::note info
pushは2回行います。`git push`の後に続く文字に注意してください。
* GitHubリポジトリ
* Herokuデプロイ専用リポジトリ
:::
```
git add .
git commit -m "Herokuへデプロイ"

# リモートブランチの特定のブランチへpush
git push origin feature/products

# Herokuのリポジトリへ変更をpush
git push heroku feature/products:main
```
この手順を踏むことで、ローカルの変更がGitHubの管理リポジトリに反映されるだけでなく、Herokuにもデプロイされてアプリケーションが最新の状態に更新されます。

## デプロイが成功したか確認
本当にデプロイ成功したか確認しましょう
```
heroku ps
=== web (Basic): gunicorn config.wsgi --log-file - (1)

# 以下の出力結果は一例です
web.1: up 2025/01/21 22:12:43 +0900 (~ 2h ago)
```
`web`というのは`Procfile`で定義した「プロセス」です。
ちなみに`release`を確認するには以下の2つのどちらかのコマンドを試しましょう
```
heroku logs --dyno release --tail
# command + C で処理から抜ける
```
```
heroku run python manage.py showmigrations
Running python manage.py showmigrations on ⬢ boiling-everglades-98681... up, run.9170
admin
 [X] 0001_initial
 [X] 0002_logentry_remove_auto_add
 [X] 0003_logentry_add_action_flag_choices
```

## URLでアプリにアクセス
以下のコマンドもしくは、`heroku create`した際に実行結果として返ってきた`~~hreokuapp.com`のURLからアクセス可能です。
```
heroku open
```
ただ、以下の画像のようにエラーが出力されているかと思います。
`python manage.py runserver`で出力されて結果はまだ反映されていません。

![Herokuエラー1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3918502/6752d84c-db8d-06bb-f961-02f0c550cbda.png)

:::note info
デプロイ自体は成功しています。
以下のURLよりHerokuのページに行くとアプリが作成されています。
`heroku open`でブラウザに開かれた
`https://`以降の部分がアプリ名となっています。
https://dashboard.heroku.com/
:::
# ログを表示
先ほどの画像にもある通り、以下のコマンドで`heroku open`した際のログを出力できます。
```
heroku logs --tail
```
command + C で停止できます。

# 何が足りないのか
これから何が足りないのか説明します。
- 本番環境の動作への適応
- 静的ファイルの配信
- URLやアップロードファイルの管理

この3点を満たすことで本番環境のURLからローカルと同じ動作をすることが可能です。
# `settings.py`へ追記
以下のコードを`setttings.py`に追記

:::note info
- 記載順序はご自身に任せます
- **...**　←これはコードの続きです
:::

```python:settings.py
import os
...
...
# 本番環境適応時はDEBUGをFalseにする
DEBUG = False
# ホストにHerokuを追加
ALLOWED_HOSTS = ['xxyyzz.com']
...
...
MIDDLEWARE = [
    # SecurityMiddlewareの後、django.contribの前に記載
    'django.middleware.security.SecurityMiddleware',
    'whitenoise.middleware.WhiteNoiseMiddleware',    # ここに追加
    'django.contrib.sessions.middleware.SessionMiddleware',
    'django.middleware.common.CommonMiddleware',
    'django.middleware.csrf.CsrfViewMiddleware',
    'django.contrib.auth.middleware.AuthenticationMiddleware',
    'django.contrib.messages.middleware.MessageMiddleware',
    'django.middleware.clickjacking.XFrameOptionsMiddleware',
    ...
]
...
...
# 以下4行は#CSSファイルの設定
STATIC_ROOT = os.path.join(BASE_DIR, 'staticfile')
STATIC_URL = '/static/'
STATICFILES_DIRS = [os.path.join(BASE_DIR, 'static')]
# whitenoiseを設定
STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage'
...
...
# URLの末尾にスラッシュを自動的に追加する
APPEND_SLASH = True
# メディアファイルのURL設定
MEDIA_URL = '/media/'
# メディアファイルの保存先ディレクトリ
MEDIA_ROOT = os.path.join(BASE_DIR, 'media')
# Cloudinaryを使ってメディアファイルを保存する設定
DEFAULT_FILE_STORAGE = 'cloudinary_storage.storage.MediaCloudinaryStorage'

# Cloudinaryの認証情報を環境変数から取得する
CLOUDINARY_STORAGE = {
    'CLOUD_NAME': env('CLOUDINARY_NAME'),
    'API_KEY': env('CLOUDINARY_API_KEY'),
    'API_SECRET': env('CLOUDINARY_API_SECRET'),
}
```

# 必要なものをインストール
必要なモジュールを`pip`でインスールしましょう

:::note warn
後ほどエラーが出たらその都度インストールしましょう
:::
### Cloudinaryをインストール（外部ストレージ上で画像を管理）
```
pip install cloudinary==1.42.1
```
### django-herokuをインストール
```
pip install django-heroku==0.3.1
```
### pillowをインストール（アプリ内で画像を取り扱う）
```
pip install pillow==11.1.0
```
### whitenoiseをインストール（静的ファイルの取り扱い）
```
pip install whitenoise==6.8.2
```


# `requirements.txt`を確認
Procfileと同階層に`requirements.txt`ファイルを作成しましょう
```
# requirement.txtを作成と必要なモジュールを記載
$ pip freeze > requirements.txt
```
そうすると以下のような記述が
`requirement.txt`に記載されます。

:::note warn
* いらないモジュールも含まれているかもしれませんので参考程度にご覧ください
* コメントは外してください
:::
```requirements.txt
certifi==2024.12.14            # SSL証明書を確認するためのライブラリ
charset-normalizer==3.4.1      # テキストのエンコーディングを正規化
cloudinary==1.42.1            # Cloudinaryと連携するためのライブラリ
Django==4.2.5                 # Djangoのメインフレームワーク
django-environ==0.11.2        # 環境変数を管理するライブラリ
django-heroku==0.3.1          # Heroku向けのDjango設定を自動化
gunicorn==23.0.0              # WSGI HTTPサーバー（本番用）
pillow==11.1.0                # 画像処理用のライブラリ
psycopg==3.1.12               # PostgreSQL用の最新データベース接続ライブラリ
psycopg2==2.9.10              # PostgreSQL用の旧データベース接続ライブラリ
sqlparse==0.3.0               # SQL文を解析・整形するためのライブラリ
whitenoise==6.8.2             # 静的ファイルを効率的に提供するミドルウェア
```
# 環境変数をherokuへ接続
今回設定するのは以下の3種類で5つの環境変数を設定します。
* シークレットキー
* データベースURL
* Cloudinary環境変数

Cloudinaryのことに関しては以下のサイトで理解できるかと思います。

https://zenn.dev/hathle/articles/django-cloudinary

:::note warn
本来はlocalで確認したほうがいいですが、割愛します
:::
以下のコマンドで環境変数を設定できます。
```terminal
heroku config:set SECRET_KEY='settings.pyのSECRET_KEY'
heroku config:set DATABASE_URL='データベースURL'
heroku config:set CLOUDINARY_API_KEY='Cloud name'
heroku config:set CLOUDINARY_API_SECRET='API Key'
heroku config:set CLOUDINARY_NAME='API Secret'
```
:::note
push完了後に以下の画像の部分で確認できます
アプリ選択 → settings → Config Vars

![スクリーンショット 2025-01-28 1.50.55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3918502/f469a1cb-a83a-00af-6281-34a99fe3fd6b.png)
:::
# エラー後に再度変更を`commit`してデプロイ
```
git add .
git commit -m "Herokuへデプロイ"

# リモートブランチの特定のブランチへpush
git push origin feature/products

# Herokuのリポジトリへ変更をpush
git push heroku feature/products:main
```
# エラーが出力されていないか再度アクセス
```
heroku open
```

これでアクセスされたら成功です。
## またエラーになったら...
もしかするとまたエラーになっているかもしれませんが、
その際は以下の2点に注目して愚直にエラーと向き合いましょう
* `git push heroku` のコマンドの後にエラーがないか
* `heroku logs --tail` でログを表示

# 管理者でログイン
デプロイしてアプリが出力できたら`/admin`にアクセスして
管理者でログインできるようにしておきましょう。
```
$ heroku run python manage.py createsuperuser
```

いかがでしたか？
かなり長かったと思いますが、これでネット上に公開できます。

私は2日かけてできずにスーパーエンジニアの方と
ペアプロしながらできました。