Django Heroku デプロイ２

#前提
前回の記事「Django Heroku デプロイ１」の続きです。
https://qiita.com/yusuke_mrmt/items/1a3ee727d119617b2d85

#デプロイ
ここまでで、アプリをデプロイする領域を確保できたので、実際にデプロイまでいきます。

##デプロイまでの流れ
デプロイを行うにあたり、事前に以下の項目を設定する必要があります。

１、Heorku用の設定ファイルの作成
２、アプリケーションサーバーのインストール
３、django-herokuインストール
４、データベースの設定
５、許可ホストの設定
６、静的ファイルの設定
７、開発環境の設定と本番環境の設定の分離

上記となります。順に設定していきます。


###１、Heorku用の設定ファイルの作成
プロジェクトのディレクトリ直下に下記の2つのファイルを追加します。<br>
■runtime.txt の作成
runtime.txt は、Herokuで使用するPythonのバージョンを指定するファイルです。 
プロジェクトのディレクトリ直下（ manage.py と同じディレクトリ）に、以下の内容で保存してください。

```runtime.txt
python-3.7.3
```
<br>
■Procfile の作成
Procfile とは、Heroku上で実際に実行するコマンドを記述するファイルのことです。<br>
以下の内容で保存してください。

```procfile
web: gunicorn config.wsgi --log-file -
```
gunicorn というコマンドを実行しています。 
gunicorn とはPythonのアプリケーションサーバーのことです。


###２、アプリケーションサーバーのインストール
先ほど Procfile にHerokuで実行するコマンドとして記述した gunicorn を実行できるようにするために、インストールします。

```procfile
$ pipenv install gunicorn
```

###３、django-herokuインストール
Djangoの設定をHerokuに適用する作業を簡易化してくれる django-heroku というライブラリをインストールします。

```procfile
$ pipenv install django-heroku
```

###４、データベースの設定
本番環境用にデータベースの設定をします。
config/settings.py の DATABASES を以下のように設定してください。

```settings.py
...# 省略

# Database
# https://docs.djangoproject.com/en/2.2/ref/settings/#databases

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
    }
}

# 追記
import dj_database_url
db_from_env = dj_database_url.config()
DATABASES['default'].update(db_from_env)

...# 省略

```
<code>dj_database_url</code>でHeroku用のデータベース設定をインポートし、
DATABASESを上書きしています。


###５、許可ホストの設定
Djangoはホストをホワイトリスト方式で制限しています。
config/settings.py の ALLOWED_HOSTS に、アプリ名（最初にユニークな設定をしたもの）を設定してください。

```settings.py
ALLOWED_HOSTS = ["<Herokuアプリ名>.herokuapp.com"]
```
これを設定しない場合、Djangoのセキュリティ機能によってアプリにアクセスできなくなってしまいます。
↓↓例

```settings.py
ALLOWED_HOSTS = ["banban-2020.herokuapp.com"]
```

###６、静的ファイルの設定
デプロイ時に静的ファイルの設定の箇所でつまづいてしまうため
config/settings.py の静的ファイルの設定を下記のように追記しましょう。

```settings.py
...# 省略

# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/2.2/howto/static-files/

STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles') # 追記
STATIC_URL = '/static/'

# 追記
STATICFILES_DIRS = (
    os.path.join(BASE_DIR, 'static'),
)

...# 省略

```

次に、プロジェクトのルートディレクトリにstaticディレクトリを作成し、
その中に<code>.gitkeep</code> という名前で空のファイルを作成しておきます。

　　　　　　　　　　・
　　　　　　　　　　・
　　　　　　　　　　・

【補足】<code>.gitkeep</code>を追加する理由
Herokuで静的ファイルを配信するのに
staticディレクトリを作成しておかなければならないのですが、
空のディレクトリはgitで管理できないため、.gitkeepという空のファイルを作成しています。
.gitkeep という名前でなくても構いませんが、
このような空のディレクトリをgitで管理したい場合は、
慣習的に.gitkeepという名前をつけることが多いみたいです。


###７、開発環境の設定と本番環境の設定の分離
最後に、これまで設定した内容を開発環境と本番環境で分離します。
config/settings.pyの末尾に下記を追記してください。

```settings.py
...
...
...# 省略

DEBUG = False

try:
    from config.local_settings import *
except ImportError:
    pass

if not DEBUG:
    import django_heroku
    django_heroku.settings(locals())

```
django_heroku.settings(locals()) でdjango_herokuがHeroku用に設定を適用してくれます。

次に、Herokuにはデプロイしない開発環境用の設定ファイルを作成します。
config/local_settings.py を作成し、下記の内容で保存してください。

```local_settings.py
import os
BASE_DIR = os.path.dirname(os.path.dirname(__file__))

DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
    }
}

ALLOWED_HOSTS = []

DEBUG = True

```
そしてプロジェクトの直下（ manage.py と同じディレクトリ）に .gitignoreファイルを作成し、下記の内容で作成してください。

```.gitignore
staticfiles
config/local_settings.py
db.sqlite3
```
.gitignore は、gitで管理しないファイルを宣言したファイルです。 
ここで記述した config/local_settings.py はHerokuにはプッシュされないことになります。
つまり、本番環境には反映されない開発環境用の設定となります。

以上でデプロイを行うまでの修正は終わりです。

#Herokuにデプロイする
ターミナルにて以下コマンドのようにgitでプッシュを行うことでデプロイが可能です。

```.gitignore
$ git add .
$ git commit -m "initial commit"
$ git push heroku master
```
↓↓上記の<code>$ git push heroku master</code>を入力するし上手くいくと下記のようになります。

```.gitignore
$ git push heroku master
Total 0 (delta 0), reused 0 (delta 0)
remote: Compressing source files... done.
remote: Building source:
remote:
remote: -----> Python app detected
remote: -----> Installing python-3.7.3
remote: -----> Installing pip
remote: -----> Installing dependencies with Pipenv 2018.5.18…
remote:        Installing dependencies from Pipfile.lock (8f0d8d)…
remote: -----> Installing SQLite3
remote: -----> $ python manage.py collectstatic --noinput
remote:        120 static files copied to '/tmp/build_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx/staticfiles'.
remote:
remote: -----> Discovering process types
remote:        Procfile declares types -> (none)
remote:
remote: -----> Compressing...
remote:        Done: 64.9M
remote: -----> Launching...
remote:        Released v1
remote:        https://banban-2020.herokuapp.com/ deployed to Heroku
remote:
remote: Verifying deploy... done.
To https://git.heroku.com/banban-2020.git
 * [new branch]      master -> master
```

次に、ターミナルにて以下コマンドでmigrationを行います。
ローカル環境で行なっていた pipenv run python manage.py migrateコマンドを
本番環境でも行うというイメージです。

```.gitignore
$ heroku run python manage.py migrate

Running python manage.py migrate on ⬢ banban-2020... up, run.8064 (Free)
Operations to perform:
  Apply all migrations: admin, auth, contenttypes, banban, sessions
Running migrations:
  Applying contenttypes.0001_initial... OK
  Applying auth.0001_initial... OK
  Applying admin.0001_initial... OK
  Applying admin.0002_logentry_remove_auto_add... OK
  Applying admin.0003_logentry_add_action_flag_choices... OK
  Applying contenttypes.0002_remove_content_type_name... OK
  Applying auth.0002_alter_permission_name_max_length... OK
  Applying auth.0003_alter_user_email_max_length... OK
  Applying auth.0004_alter_user_username_opts... OK
  Applying auth.0005_alter_user_last_login_null... OK
  Applying auth.0006_require_contenttypes_0002... OK
  Applying auth.0007_alter_validators_add_error_messages... OK
  Applying auth.0008_alter_user_username_max_length... OK
  Applying auth.0009_alter_user_last_name_max_length... OK
  Applying auth.0010_alter_group_name_max_length... OK
  Applying auth.0011_update_proxy_permissions... OK
  Applying banban.0001_initial... OK
  Applying banban.0002_card... OK
  Applying sessions.0001_initial... OK
```

完了したら<code>https://Herokuアプリ名.com/アプリ名/</code> にアクセス！！


以上でデプロイが完了です。

#自分がデプロイ時にエラーになったこと
<b>３、django-heroku のインストール時にエラー</b>
<code>$ pipenv install django-heroku</code>をターミナルに入力すると・・・
とてつもなく長いエラー文が出てきましました。
エラー文の最後の方に下記のような文章がありました。

```.gitignore
ERROR: ERROR: Package installation failed...
  ☤  ▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉▉ 0/1 — 
```

###解決した方法
ターミナルにて下記、コマンドを入力しました。

```
xcode-select --install
env LDFLAGS="-I/usr/local/opt/openssl/include -L/usr/local/opt/openssl/lib" pipenv install psycopg2
```
※参考記事
https://stackoverflow.com/questions/39767810/cant-install-psycopg2-package-through-pip-install-is-this-because-of-sierra

----------------------------------------------------------
<b>Heroluデプロイ時にエラー</b>
ターミナルにて<code>$ git push heroku master</code>を入力すると下記のようなエラーが発生。

```
$ git push heroku master

Enumerating objects: 93, done.
Counting objects: 100% (93/93), done.
Delta compression using up to 8 threads
Compressing objects: 100% (82/82), done.
Writing objects: 100% (93/93), 21.51 KiB | 2.39 MiB/s, done.
Total 93 (delta 33), reused 0 (delta 0)
remote: Compressing source files... done.
remote: Building source:
remote: 
remote:  !     No default language could be detected for this app.
remote: 			HINT: This occurs when Heroku cannot detect the buildpack to use for this application automatically.
remote: 			See https://devcenter.heroku.com/articles/buildpacks
remote: 
remote:  !     Push failed
remote: Verifying deploy...
remote: 
remote: !	Push rejected to banban-2020.
remote: 
To https://git.heroku.com/banban-2020.git
 ! [remote rejected] master -> master (pre-receive hook declined)
error: failed to push some refs to 'https://git.heroku.com/banban-2020.git'
muramatsuyuusukenoMacBook-Pro:banban muramatsuyuusuke$ git add .
muramatsuyuusukenoMacBook-Pro:banban muramatsuyuusuke$ git commit -m "initial"
On branch master
Your branch is up to date with 'origin/master'.
```

###解決した方法
requirements.txtを生成すると解決しました。
ターミナルにて下記、コマンドを入力し再度Herokuへpushすると問題なく通りました。

```
pipenv lock -r > requirements.txt
```

以上です。