Python
Django
Heroku
wsgi
HerokuCLI

初心者がハマった、初めてのDjangoアプリのHerokuデプロイ

More than 1 year has passed since last update.

諸用事あって、ローカルで作っていたDjangoアプリをHerokuにデプロイすることになったのですが……なにやらやたらと時間がかかってしまったので、その忘備録として

環境

  • Windows10
  • Django 1.11.x
  • python2.7.x

Djangoとは

(こんな記事を見ている時点で知ってはいると思いますが……)DjangoはPython2,3で動くWebフレームワーク……らしい
「らしい」というのは、筆者はWebフレームワークのことを良く知らないが故である。興味のある人は、公式ドキュメントを参考にしてみては……?

参考:Djangoドキュメント

デプロイとは

ローカル環境で作っていたものを、実際の本番環境へ移行すること……たぶん。
DjangoのようなWebアプリは、ただのWebサイトアップロードのように簡単ではなかった……以下、今回のデプロイのおおまかな流れを綴ってゆく。

前提条件

Djangoの習得がまだ……という方は、こちらにいい感じの記事がありましたのでこちらから習得を……!
PythonのWebアプリケーション(Django)を初心者にもわかりやすく解説
Djangoを最速でマスターする

  • 一般的なネットワーク環境(ファイルをアップロードするので)
  • Djangoアプリ完成済み(sqlite3データベース使用)(virtualenv使用済み)
  • CMD(コマンドプロンプト)が使える
  • gitインストール済み
  • Herokuアカウント所有済み

デプロイ

初めてDjangoを用いてWebアプリを作った筆者。
ぬるぬる動くぜDBの動いているぜヒャッホーイとしていたある日、ついにオンラインで動かす日が来たのであった……(物語風)

初期状況

DjangoApp/
 ├ env/ (virtualenv)
 ├ manage.py
 ├ db.sqlite3
 ├ DjangoApp/(最初からあるやつ)
 │ ├ wsgi.py
 │ └ setting.py
 └ App/ (アプリケーション本体)

デプロイ前夜

デプロイをするにあたって、必要なことは何かと調べてみると「.gitignoreにdb.sqlite3(sqliteのデータベース本体)と*.pyc、そしてenv/以下を入れておく」とのこと。
素直に.gitignoreを作り、「git rm -r --cached .」でキャッシュを削除してcommitした

参考: 【Qiita】.gitignore の設定を反映させる

前夜後の状況

DjangoApp/
 ├ env/
 ├ manage.py
 ├ db.sqlite3
 ├ DjangoApp/
 │ ├ wsgi.py
 │ └ setting.py
 ├ App/
 └ .gitignore (New)

デプロイ準備開始

Herokuにファイルをアップロードする際には、どうやら「Procfile(拡張子なし)」「requirements.txt」「runtime.txt」の3種の神器が必要とのことで作成に入る

Procfile

Herokuに「このアプリはこうやって起動してね!☆」というのを伝えるもの(らしい)
とりあえず、いろんな人が書いていた通りに書いてみることにした

Procfile
web: gunicorn DjangoApp.wsgi --log-file -

DjangoAppの部分は各自のアプリケーション名を記述する。
そして、後でこの「gunicorn」とやらをvirtualenvにインストールしないといけないそうで……むぅ、ひと手間ふた手間かかる……

requirements.txt

Herokuに、このアプリケーションを動かすのに必要なモジュールを連絡する必要がある。それに使うのがrequirements.txt。(たぶん)
こいつは簡単。コマンドでvirtualenvを起動したのちに

CMD
pip freeze > requirements.txt 

とすればよい。
以下のようなファイルが出来上がっているはず。

requirements.txt
Django==1.11.1
virtualenv==15.1.0

runtime.txt

Herokuに、今使っているpythonのバージョンを伝えるものだそうで……(こいつもよくわからない)

CMD
python-2.7.x

2.7.xの部分は各自のパソコンにインスツールされているPythonのバージョンをば

三種の神器作成後

DjangoApp/
 ├ env/
 ├ manage.py
 ├ db.sqlite3
 ├ DjangoApp/
 │ ├ wsgi.py
 │ └ setting.py
 ├ App/
 ├ requirements.txt (New)
 ├ runtime.txt (New)
 ├ Procfile (New)
 └ .gitignore

Django-toolbeltのインストール

調べたところ、デプロイには様々な追加モジュールが必要だそうで……先のgunicorn、staticfileを扱うためのdjando-static、データベースを扱うdj_database_urlなど……一つ一つ扱うのが面倒だねーと思っていたところ、なんとこれらをまとめてやってくれる味方を発見!django-toolbeltである……!

CMD
pip install django-toolbelt

としてtoolbeltをインストールする。必要なモジュールは全部そろった……!

……聡明なあなたなら、「こやつ(笑)」と気が付くはず……そう、このタイミングでtoolbeltによってインストールされたモジュールはrequirements.txtに書いていないのである……筆者はこれに気が付かず半日以上も時間を費やしたのであった……
正しくは、このタイミングでもう一度pip freezeをし直してrequirements.txtをアップデートをしましょう!!

toolbeltインストール後のrequirements.txt

requirements.txt
dj-database-url==0.4.2
dj-static==0.0.6
Django==1.11.1
django-toolbelt==0.0.1
gunicorn==19.7.1
psycopg2==2.7.3
pytz==2017.2
static3==0.7.0
virtualenv==15.1.0

まだまだ続くよ!デプロイ準備!

次はデータベース周りのお話。

聞くところによると、Herokuではsqlite3データベースが使えない模様(インストールできないのかしら……?)
そのため、Heroku標準搭載されている(?)Postgresを使う必要がある、とのこと……

……そして、そのためのdj-database-urlである!
setting.pyを開いて、以下の部分を見つけてくる。

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

このDBに関する記述を、以下のように書き換える

setting.py
if "COMPUTER-NAME" in hostname:
    # デバッグ環境
    # DEBUG = True // 筆者はデバッグモードの切り替えもここでやってしまった
    DATABASES = {
        'default': {
            'ENGINE': 'django.db.backends.sqlite3',
            'NAME': os.path.join(BASE_DIR, 'db.sqlite3'),
        }
    }
    ALLOWED_HOSTS = [] # よくわからんけど、これも大事らしい
else:
    # 本番環境
    # DEBUG = False
    import dj_database_url
    db_from_env = dj_database_url.config()
    DATABASES = {
        'default': dj_database_url.config()
    }
    ALLOWED_HOSTS = ['*']

こうすると、ローカル環境ではsqlite3を、Heroku環境下ではPostgresを用いてくれる。(らしい)COMPUTER-NAMEは各自のデバイスの名前で。(localと書けば良い、と見かけたのですが自分の環境ではだめでした……)
また、(ほぼ確実に)「hostnameが無いじゃないか馬鹿ヤロメっ!」と怒られた場合はsetting.pyの最初の方に以下を追加しておきましょう。

setting.py
.
from socket import gethostname
hostname = gethostname()
.

wsgiってよくわからん

ローカル環境では一切触ってこなかったwsgi.pyをつい触る時が……!
……と思いきや、以下のように本当に少し弄るだけでおkらしい

setting.py
import os
from dj_static import Cling
from django.core.wsgi import get_wsgi_application

os.environ.setdefault("DJANGO_SETTINGS_MODULE", "DjangoApp.settings")
application = Cling(get_wsgi_application())

DjangoAppは各自のプロジェクト名をば。
……結局、wsgi.pyとはなんだったのか(ソレガワカラナイ

最終状況

DjangoApp/
 ├ env/
 ├ manage.py
 ├ db.sqlite3
 ├ DjangoApp/
 │ ├ wsgi.py (modified)
 │ └ setting.py (modified)
 ├ App/
 ├ requirements.txt
 ├ runtime.txt
 ├ Procfile
 └ .gitignore

いざデプロイ!目指すはHeroku!

準備が整ったのでCMDを立ち上げてHerokuへ!

CMD
git add .
git commit -am "Launching now!"

heroku login
> Enter your Heroku credentials.
> Email: hoge@huga.com
> Password (hidden):
> Logged in as hoge@huga.com

heroku create NAME
> https://NAME.herokuapp.com/ | https://git.heroku.com/NAME.git

git push heroku master
> なんか長々としたsomething

heroku run python manage.py migrate
> いつものmigrateコマンドが流れる

heroku open

最後のHeroku openでアプリケーションが開いてくれます。(筆者の場合は初っ端Application Errorでしたけどね……)
以上でおしまい!あとは運用ですね!!お疲れ様でした!!!(疲れた

感想

こんなことに感想を書くのもあれですが……初心者からすると「なんと遠回りな……もっとスマートにできないの??」と思ってしまうくらい大変でした……(´;ω;`)
特に、最新(ver1.11)のリファレンスが少なく……結局、公式の英語ドキュメント読みまくってました(笑)
広がれ、Djangoの輪!

未解決の不具合

壊れる管理サイト

無事Herokuにデプロイ完了……と思いきや、管理サイトが……

無題.png

壊れるなぁ……(´・ω・`)なぜかCSSを読み込んでくれない……
staticファイルを扱うように、setting.pyに以下を追加しましたが……ダメ……っ!

setting.py
STATIC_URL = '/static/'

# Static asset configuration
STATIC_ROOT = 'staticfiles'

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

……ま、気長に修正していきます!