DjangoをHerokuに上げる際にハマるポイント（データベース、静的ファイル）

　[Django](https://qiita.com/hibit/items/d9b2ed158ac3b0ff7b64)の続き。

# とりあえずデータベース

　大体のWebサービスにおいてはデータベースは必要になると思います。Djangoにおいてはデフォルトでsqliteがデータベースとして設定されており、マイグレートするだけで`db.sqlite3`というデータベースファイルが出来上がり、これに読み書きすることでサービスを実現できます。

　しかし、これをこのままHerokuにデプロイしても上手くいきません。Heroku環境にもそのまま`db.sqlite3`が移植され、データベースとしてはそれを参照することになりますが、これは**Gitに紐付かないデータなので、せっかくデータを書き込んでも定期的に初期状態に戻ってしまう**のです。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/288125/981954cd-ac2f-8e4e-fefc-524bba9f39fe.png)

　悲しい。

　これを防ぐためには、Gitファイルの外にあるデータベース、というかHerokuに備え付けられているPostgresに接続する必要があります。しかし、繰り返しになりますがDjangoの初期設定はそうなってはいません。かといって、ローカル環境ではHerokuのPostgressは使えないので、同一の設定で2つの環境を行き来することは原理的に不可能ということになります。

# ローカルとリモートの切り分け

　しかし、ある工夫、というかアドホックな修正を行えば、この問題を解決することができます。ローカル環境だけにしか存在しないファイルを設定し、そのファイルがある時だけif文でコードを変えればよいのです。これは、`.gitignore`を使ってリモートに上げないファイルを選択することにより実現することができます。具体的には、ローカルフォルダに例えば`local.py`というファイルを置いておきます。これは**中身が空でもいい**ですが、アクセストークン等の重要情報を秘匿するのにちょうどよいので、そのような情報を乗せると便利でしょう。逆に、リモート環境では`local.py`にある情報を取り出せないので、環境変数でアクセストークンを指定する必要がありますが、後に載せるコードではそれも実装します。

　フォルダ構成について想像しづらい人もいるかもしれないので実例を。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/288125/2c15cd2c-5c18-8341-7aeb-a4e8b804eb21.png)

　これは実際に私が管理している[リポジトリ](https://github.com/hibit-at/takenyan-analyzer)ですが、`local.py`とそれに関わるフォルダ構成の様子です。また、`db.sqlite3`と`local.py`のファイル名が灰色になっていることに気づくと思いますが、これは`.gitignore`の対象になっていることを示しています。当然ながら、リンク先のgithubにはこの2つのファイルは存在しません。

　そして、`settings.py`では以下のようなif文で、諸々のパラメータを分岐させます。

```python
import dj_database_url

if os.path.exists('local.py'):
    DEBUG = True
    ALLOWED_HOSTS = ['*']
    from local import SK
    SECRET_KEY = SK
    DATABASES = {
        'default': {
            'ENGINE': 'django.db.backends.sqlite3',
            'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
        }
    }
else:
    DEBUG = False
    ALLOWED_HOSTS = ['*']
    SECRET_KEY = os.environ['SK']
    db_from_env = dj_database_url.config()
    DATABASES = {
        'default': dj_database_url.config()
    }
```

　本件とは関係ないですが、ローカルと本番でDEBUGを切り替えるのも、ついでにやってもいいでしょう。

　これで、いい感じにローカルとリモートを切り分けた上でスムーズに開発していくことができます。

# 静的ファイル

　画像ファイルやCSS等は、静的ファイルとしてフォルダ内の特別な場所に置く必要があります。この辺りは、`settings.py`で

```python
STATIC_URL = '/static/'
```

　あたりに指定されているのですが、実は**全然設定が足りていません**。

　まず、ローカル環境で静的ファイルを置く場所を指定しなければいけません。これは例えば、

```python
STATICFILES_DIRS = (
    os.path.join(BASE_DIR, 'static'),
)
```

　という変数を足せば大丈夫です。この例だと、プロジェクトファイル直下に`static`というフォルダを作れば、それが参照場所となります。

# COLLECTSTATIC

　しかし、このままだと**実はデプロイできません**。Herokuの初期設定だと、デプロイ時に`COLLECTSTATIC`という儀式を経る設定になっていますが、Djangoの初期設定ではそれに失敗するからです。静的ファイルを一切使わないならば、**そもそも`COLLECTSTATIC`をしない**という選択も取れますが、静的ファイルを扱うつもりならばこの問題に正面から向き合う必要があります。COLLECTSTATICについては[このページ](https://qiita.com/saira/items/a1c565c4a2eace268a07)が超わかりやすいのでおすすめです。

　なお、COLLECTSTATICをオフする手段については、Heroku環境内（`heroku run bash`で入れます）で

```
heroku config:set DISABLE_COLLECTSTATIC=1
```

　というコマンドを打つことにより設定できますし、WebブラウザのGUIでも設定することができます。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/288125/5ff55e31-6d23-cee8-5e25-e88ec09ab7de.png)

　さて、`COLLECTSTATIC`を成功させるには、`settings.py`を以下のように設定する必要があります。なおここら辺の設定は[公式ドキュメント](https://devcenter.heroku.com/ja/articles/django-assets)に詳しく載っています。

```python
STATIC_ROOT = os.path.join(BASE_DIR, 'staticfiles')
STATIC_URL = '/static/'

STATICFILES_DIRS = (
    os.path.join(BASE_DIR, 'static'),
)
```

　これで無事にデプロイできるはずです。

　**しかしまだ静的ファイルは使えません**。

# Whitenoise

　リモート環境では、ファイル名そのままではパスの指定に失敗します。なぜなのか。

　結論から言うと、リモート環境の静的ファイルは、ファイル名の途中に謎のハッシュ値みたいのがついています。

`https://hoge.herokuapp.com/static/fuga.28487bbbe8b5.png`

　こんな感じで。ではどうすればいいかというと、`Whitenoise`というソフトがこれをいい感じに処理してくれるらしいです。上のドキュメントに載っているので思考停止で載せましょう。

```python
pip install whitenoise
```

　した後で（`requirements.txt`に書き加えておくのも注意！）、`settings.py`に以下を追加。

```python
MIDDLEWARE_CLASSES = (
    'whitenoise.middleware.WhiteNoiseMiddleware',
    ...

STATICFILES_STORAGE = 'whitenoise.storage.CompressedManifestStaticFilesStorage'
```

　これでやっとうまくいきます。テンプレートhtml内において`{% static 'hoge.png' %}`といった形で表記された部分は、ローカル環境では`static/hoge.png`、リモート環境では`static/hoge.**********.png`といい感じにレンダーされてくれます。

　これでやっとリモート環境で静的ファイルを利用できます。書き足すことが必要な設定多すぎん？