Renderにデプロイ（Django）

**Herokuの無料枠が無くなったので、デプロイ環境をRenderに移す**
- まずは、以下のレポジトリからソースをダウンロードする。
- これをもとにRenderへのデプロイ方法を紹介していく。

https://github.com/Keito777/daily_report

- ダウンロードファイルを開いて、その場で以下のコマンドを実行する（仮想環境作成）
- 本記事では、仮想環境名をmyenvにしている。
```:mac専用
$ python3 -m venv 仮想環境名
$ source 仮想環境名/bin/activate
```

※以下の記事で、アプリの作成方法を説明している。

https://qiita.com/kkk777/items/555296a88a2b871b1403　

# デプロイの流れ要点
- デプロイ前準備
    - 既存ファイルの削除
    - 既存ファイルの修正
    - 新規ファイル作成（Renderデプロイ用）
    - Djangoのカスタムコマンドを作る
- GitHubにコミットする
- RenderとGitHubを連携する
- Renderにデプロイ

# Renderについて
RenderはHerokuと同様、PaaS型のクラウドプラットフォーム。
RDBMSも両者PostgreSQLになっている。
また、次の記事「[【格安本番運用が可能に】Render.com のメリット・デメリットを Heroku と比較してみた](https://zenn.dev/mc_chinju/articles/compare_render_and_heroku)」では、Herokuとの違いやデプロイ時の詳細設定について解説している。

個人的に、RenderはHerokuを意識している印象。

# デプロイ前準備

## 既存ファイルの削除

以下の既存ファイルを削除する。
- Procfile(Heroku専用)
- runtime.txt(Heroku専用)
- report_project/settings_local.py
- README.md(pushに時間がかかってしまうので)


## 既存ファイルの修正

以下の既存ファイルを修正する。
- requirements.txt
- settings.py
- .env

### requirements.txt
```:requirements.txt
asgiref==3.5.2
dj-database-url==1.0.0
dj-static==0.0.6
Django==4.1.2
django-environ==0.9.0
django-model-utils==4.2.0
gunicorn==20.1.0
psycopg2-binary==2.9.5
python-decouple==3.6
python-dotenv==0.21.0
sqlparse==0.4.3
static3==0.7.0
whitenoise==6.2.0
```
ローカル環境で動作確認を行う場合は、以下のコマンドを実行して、仮想環境上（ここでは、myenv）にライブラリをインストールする。

※仮想環境の作成方法：[仮想環境の作成とactivate](https://qiita.com/kkk777/items/56f93a20e85920024e29)
```
(myenv) $ pip install -r requirements.txt
```

### settings.py
- 以下、4点の修正を加える
    - ALLOWED_HOSTS
    - DB設定
    - staticファイル
    - .envファイルからの環境変数読み込み
    - Herokuの設定を解除

```python:settings.py
ALLOWED_HOSTS = ['127.0.0.1', 'daily-report.onrender.com']
```
デプロイ完了後のアプリのURLを指定する。つまり、`daily-report.onrender.com`のところを独自のアプリケーションURLにする。

```python:settings.py（修正前）
DATABASES = {
    'default': {
        'ENGINE':'django.db.backends.postgresql_psycopg2', # PostgreSQL使いますよ宣言
        'NAME': 'appdb', # データベース名
        'USER': 'postgres', # データベースに接続するDBユーザー名
        'PASSWORD': 'password1', # データベースに接続する際のDBユーザのパスワード
        'HOST': '', # 'localhost'
        'PORT': '', # 5432
    }
}
```
既存のものを以下に変更する。

```python:setting.py
import dj_database_url
# ~省略~
default_dburl = "sqlite:///" + str(BASE_DIR / "db.sqlite3")
DATABASES = {'default': dj_database_url.config(default=default_dburl)}
# postgresqlを設定する場合の書式は、postgresql://user_name:password@localhost:port/database_name（以下では、既存のものを書籍に当てはめている）
# DATABASES = {'default': dj_database_url.parse('postgresql://postgres:password1@localhost:5432/appdb')}

```

この設定にすると、データベースの設定は、「DATABASE＿URL」という環境変数から読み込むようになります。これで、環境変数の指定を外部から変更できるようになります。
また、環境変数の指定がない場合には、Django の標準設定である、「SQLite」を利用します。
※Renderにデプロイすると自動的に、「DATABASE＿URL」の値が設定される。

```python:settings.py
STATIC_URL = 'static/'
STATICFILES_DIRS = (os.path.join(BASE_DIR, 'static'),)
STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')
# 以下を追加
STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage'
```
STATIC_ROOT環境変数の下に追加

作成したnewsuperuserコマンド実行時に、settings.pyから値を取得するように定義したので、以下を追加する。
```python:settings.py
dotenv.load_dotenv() # .env ファイルを読み込む
SECRET_KEY = os.getenv('SECREST_KEY') # .env内の環境変数を取得
SUPERUSER_NAME = os.getenv('SUPERUSER_NAME')
SUPERUSER_EMAIL = os.getenv('SUPERUSER_EMAIL')
SUPERUSER_PASSWORD = os.getenv('SUPERUSER_PASSWORD')
```
末尾のHerokuに関する設定をコメントアウト、または削除する。
```python:settings.py
DEBUG = False # デプロイ時、Falseに修正
#DEBUG = True
#if not DEBUG:
    #import django_heroku
    #django_heroku.settings(locals())
```

## 新規ファイル作成（Renderデプロイ用）
- .env(環境変数設定用)
- build.sh
- render.yaml
- report/management/commands/newsuperuser.py(Renderの無料枠を使うため)

以下では、Render公式の「[Getting Started with Django on Render](https://render.com/docs/deploy-django)」に記載されているものを参考にしていく。

### .env
公に公開したくない環境変数をこのファイルに記述していく。
（.envは、.gitignoreに記述されているのでGitHub上で管理されない）
```:.env
SUPERUSER_NAME　=　admin
SUPERUSER_EMAIL　=　admin@admin.com
SUPERUSER_PASSWORD　=　password
SECREST_KEY = ~~以下の説明をもとに値を設定する~~
```
- ここでは、今回のサービスのDjangoスーパーユーザーに関する設定と重要なSECREST_KEYの設定を記述している。
- **これらの値は、settings.pyより取得される**
- **末尾の`SECREST_KEY`の値の生成と設定方法は、「[SECRET_KEY(環境変数)とは](https://qiita.com/kkk777/items/730641a0e8b0c80bdd9b#secret_key%E7%92%B0%E5%A2%83%E5%A4%89%E6%95%B0%E3%81%A8%E3%81%AF)」を確認すること。**

※SUPERUSER_EMAILは、#を先頭につけてコメントアウトしても問題ない

### build.sh
デプロイ環境上で（ビルド時）実行するコマンドを記述する。
```build.sh
#!/usr/bin/env bash
# exit on error
set -o errexit

pip install -r requirements.txt

python manage.py collectstatic --no-input
python manage.py migrate
python manage.py newsuperuser
```
- `pip install -r requirements.txt`：デプロイ環境にライブラリをインストールするため。
- `python manage.py collectstatic --no-input`：--no-inputとしているのは、staticファイルをコレクト時にyesを入力することを省略するため。
- `python manage.py migrate`：モデルをDB上に反映させるため。
- `python manage.py newsuperuser`：後の**「Djangoのカスタムコマンドを作る」**の説明のところで作成するカスタムコマンド。

## render.yaml
デプロイ環境の設定を記述する。
```render.yaml
databases:
  - name: mysite
    databaseName: mysite
    user: mysite
    region: singapore
    plan: free

services:
  - type: web
    name: mysite
    env: python
    region: singapore
    plan: free
    branch: main
    healthCheckPath: /
    buildCommand: "./build.sh"
    startCommand: "gunicorn report_project.wsgi --log-file -"
    envVars:
      - key: DATABASE_URL
        fromDatabase:
          name: mysite
          property: connectionString
      - key: SECRET_KEY
        generateValue: true
      - key: WEB_CONCURRENCY
        value: 4


    autoDeploy: true
```
- `databases`：DBの設定
    - name：Render上で表示するDB名
    - databaseName：DB名
    - user：DBユーザー名
    - region：DBサーバーの地域
    - plan：DBのプラン
- `services`：サービスの設定
    - name：サービス名（URLになる）
    - env：開発言語
    - startCommand：APサーバー(gunicorn)を起動コマンド「gunicorn 自分のプロジェクト設定dir名.wsgi」
    - envVars：環境変数の設定

**`envVars`の設定値以外は、小文字でないといけないかも**

## Djangoのカスタムコマンドを作る
Renderの無料枠では、 **「Render側のシェル機能を使用することができない」** 。
そのため、Djangoのスーパーユーザー(管理ユーザー)を事前に作成するためのコマンドを定義しておく必要がある。
以下では、「[Djangoのカスタムコマンドを作成してコマンドラインから実行する](https://qiita.com/checkpoint/items/b6947501774b4008e077)」の記事を参考に、要点を説明していく。

※**「build.sh」の説明のところで、末尾に記述している`python manage.py newsuperuser`コマンドを作成していく**

|要点|説明|
|--|--|
|①|python manage.py コマンド名を実行した時に処理する独自のコマンドを作成する。（今回はpython manage.py newsuperuserコマンド）|
|②|ディレクトリは、アプリ/management/commands/直下にコマンド名となるpythonファイルを作成する（今回はreport/management/commands/newsuperuser.py）|
|③|BaseCommandクラスを継承してカスタムコマンドを作成する|
|④|コマンド実行後の処理は、handle()メソッド内に定義する|


```python:report/management/commands/newsuperuser.py
from django.core.management.base import BaseCommand
from django.contrib.auth import get_user_model
from django.conf import settings

CustomUser = get_user_model()


class Command(BaseCommand):
    def handle(self, *args, **options):
        if not CustomUser.objects.filter(username=settings.SUPERUSER_NAME).exists():
            CustomUser.objects.create_superuser(
                username=settings.SUPERUSER_NAME,
                email=settings.SUPERUSER_EMAIL,
                password=settings.SUPERUSER_PASSWORD
            )
            print("スーパーユーザー作成に成功しました")
```
- `CustomUser = get_user_model()`：カスタムユーザーモデルを読み込む
- `CustomUser.objects.create_superuser()`：カスタムユーザーモデルのマネージャーメソッドcreate_superuser()。引数に、スーパーユーザー名, スーパーユーザーemail, スーパーユーザーpasswordを指定する。


※厳密には、create_superuser()は、カスタムユーザーモデルが継承しているUserManagerクラスのメソッド（objects = UserManager()）

参考記事：[【Django】独自ユーザーモデルの作成（カスタムUserモデル：AbstractBaseUser）](https://office54.net/python/django/model-custom-user)

# GitHubにpushする
以下の記事を参考に、リポジトリに登録してpushする。
参考記事：[レポジトリに登録してpush](https://qiita.com/kkk777/items/555296a88a2b871b1403#git%E3%81%AB%E7%99%BB%E9%8C%B2%E3%81%99%E3%82%8B)
参考記事：[一旦pushしたい場合](https://qiita.com/kkk777/items/0a81139704f3b10d072e#%E4%B8%80%E6%97%A6push%E3%81%97%E3%81%9F%E3%81%84%E5%A0%B4%E5%90%88)

# RenderとGitHubを連携する
- Renderの公式ページにアクセスする。

https://render.com/

- 右上の「DASHBOARD」をクリックする
![スクリーンショット 2022-12-09 14.42.55.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/729011/9c437dfb-eb36-332b-2db6-beaf714504c2.png)

- 「GitHub」をクリックし、連携が完了する
![スクリーンショット 2022-12-09 14.46.34.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/729011/0f32939c-4f06-20e6-058a-3bf37534d31f.png)



# Renderにデプロイ
- ナビゲーションバーの「Blueprints」をクリック
- 「New Blueprint Instance」をクリック
- GitHubのレポジトリ一覧が表示される
    - デプロイしたいものの「Connect」をクリック
- 「Service Group Name」にrender.yamlのservicesのnameの値を入力する
- 「Apply」をクリック（デプロイ開始）

**※これだけでは、Render側で環境変数を設定していないのでデプロイに失敗する。最後に以下の手順を踏む。**

## Render側（デプロイ環境）の環境変数の値を設定する
- Render公式ページのナビゲーションバー「Dashboard」をクリック
- 先ほどデプロイしたサービスが表示されていることがわかる
- デプロイしたサービス名（DBではなく）をクリック
- 「Environment」をクリック
- `PYTHON_VERSION`, `SECREST_KEY`, `SUPERUSER_NAME`, `SUPERUSER_PASSWORD`を追加し、ローカルのpythonのバージョンと.envに記載した値を入力して「Save Changes」をクリック
- 右上の「Manual Deploy」をクリックし、「Deploy latest commit」をクリック（再度デプロイ）

![スクリーンショット 2022-12-09 15.02.09.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/729011/9f537b21-ac1f-8488-f6fc-3693772e4c74.png)
- `PYTHON_VEWSION`には3.7以上を指定する必要がある。(Django==4.1.2を使用するため。)今回は、3.10.7を指定している。
- あとは、`.env`で指定した値を設定する。


**※Renderの無料枠を使用しているので、デプロイに時間がかかるが焦らずに待機する。**


# まとめ
以上が、DjangoをRenderへデプロイする方法でした。
ついつい最後のRender側の環境変数を設定し忘れてしまい、デプロイに失敗していましたが解決できてよかったです。
また、無料枠なので、デプロイに時間がかかりましたが問題なくサービスが稼働していることが確認できました。
わかりづらい点もあったかと思いますが、最後までお付き合いいただきありがとうございました。

# Appendix
## 動作確認（ローカル）
ローカルで動作確認を行う場合は、以下のコマンドを実行するように。
```
$ python manage.py makemigrations
$ python manage.py migrate
$ python manage.py runserver
```