python3: djangoを使ってheroku上にデプロイ

#やりたいこと
djangoを使ってheroku上でデプロイ。hello world出力までが今回の目的。

disclaimer: メモがてら淡々と書いているだけなので多少不親切かもしれません。
デプロイの際に参考した以下のリンクにとても親切に書かれているのでこちらを参照することをおすすめします。

<a href="https://djangogirlsjapan.gitbooks.io/workshop_tutorialjp/content/" target="_blank">Django Girls Tutorial</a>

その他参考になったリンク:

- <a href="http://qiita.com/cfiken/items/0715bb945389bc9ca682" target="_blank">heroku で python 動かすチュートリアルをやったメモ</a>
- <a href="http://qiita.com/mas9612/items/d616a74f931babae4566" target="_blank">初めてのDjango開発</a>
- <a href="http://qiita.com/nakazye/items/48651e39f07da82fe79e" target="_blank">Rubyに負けるな！HerokuでPython(Django)動かす方法</a>
- <a href="http://qiita.com/jtwp470/items/0034c85130928851ce72" target="_blank">HerokuにDjangoアプリをデプロイするときのまとめ</a>

#動作環境
python3.5.1
heroku
virtualenv

#手順
プロジェクトフォルダに移動（自分の場合は`cd Projects/github`）する。`mkdir project_name`と`cd project_name`ディレクトリ作成しそこへ移動。`python3 -m venv projedct_name`で仮想環境構築の準備。`source project_name/bin/activate` `(project_name)user$ ...`になっているのを確認。これでとりあえず仮想環境が構築されていることを確認。`cd project_name`で移動し`django-admin startproject project_name`を実行してdjangoベースのアプリケーションを作成。

```
/Users/user_name/Projects/github/project/venv_project
.
├── bin
├── db.sqlite3
├── include
├── lib
├── manage.py
├── pip-selfcheck.json
├── pyvenv.cfg
└── project

$ cd project

.
├── __init__.py
├── __pycache__
├── settings.py
├── urls.py
└── wsgi.py
```

##初期設定

```settings.py
TIME_ZONE = 'Asia/Tokyo'

USE_TZ = False
```

ちなみにデータベースはデフォルトで以下のようになっている。

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

`python3 manage.py migrate`を実行。

```
Operations to perform:
  Apply all migrations: admin, auth, contenttypes, 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 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 sessions.0001_initial... OK
```

`python3 manage.py runserver`でサーバーを動かしてみた後<a href="127.0.0.1:8000" target="_blank">127.0.0.1:8000</a>に接続。

![Screen Shot 2016-12-13 at 9.25.40 PM.png](https://qiita-image-store.s3.amazonaws.com/0/87666/90b1e968-0c11-4d21-2190-896db11b8aff.png)

ターミナル上ではこのままサーバ用にタブを放置しておくことをおすすめします。別件でタブを開いて作業をするといいかと。

##新しくモデルを作成
`python3 manage.py startapp blog`でプロジェクト内に新たに別のアプリケーションを作成。

```
.
├── bin
├── blog
├── db.sqlite3
├── include
├── lib
├── manage.py
├── pip-selfcheck.json
├── pyvenv.cfg
└── ryugakuotaku
blog
├── __init__.py
├── admin.py
├── apps.py
├── migrations
├── models.py
├── tests.py
└── views.py
```

djangoにblogアプリケーションを作ったことを教える。

```settings.py
# Application definition

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'blog'
]
```

```blog/models.py
from django.db import models
from django.utils import timezone

class Post(models.Model):
    author = models.ForeignKey('auth.User')
    title = models.CharField(max_length=200)
    text = models.TextField()
    created_date = models.DateTimeField(
            default=timezone.now)
    published_date = models.DateTimeField(
            blank=True, null=True)

    def publish(self):
        self.published_date = timezone.now()
        self.save()

    def __str__(self):
        return self.title
```

を以下のように書き換え。ここではポストメソッドを定義している。次に`python3 manage.py makemigrations blog`を実行し新しいモデルをデータベースに追加することをdjangoに伝える。

```
Migrations for 'blog':
  blog/migrations/0001_initial.py:
    - Create model Post
```

続いて`python3 manage.py migrate blog`を実行

```
Operations to perform:
  Apply all migrations: blog
Running migrations:
  Applying blog.0001_initial... OK
```

##adminの設定

```blog/admin.py
from django.contrib import admin
from .models import Post

# Register your models here.

admin.site.register(Post)
```

`admin.py`を以上のように設定した後、<a href="http://127.0.0.1:8000/admin/" target="_blank">http://127.0.0.1:8000/admin/</a>に接続してみる。

![Screen Shot 2016-12-13 at 9.43.59 PM.png](https://qiita-image-store.s3.amazonaws.com/0/87666/30d7de7d-8829-e6e9-b6ac-d979d9d68806.png)

ユーザー情報を何も登録していないので`python3 manage.py createsuperuser`でユーザー作成。`Superuser created successfully.`を確認。実際にログインしてみる。

![Screen Shot 2016-12-13 at 9.45.53 PM.png](https://qiita-image-store.s3.amazonaws.com/0/87666/406c99b9-58b2-c281-4b35-81272a04eb65.png)

実験的に実際にポストをしてみることをオススメします。

##デプロイ

デプロイとは簡単に言えばウェブ上でみんながサイトを見れるようにすること。

今回は小規模かつ無料で使えるherokuをつかってデプロイをする。

`dj-database-url, gunicorn, whitenoise`と`pip3 freeze > requirements.txt`を実行。これで`requirements.txt`というファイルが出来ているはず。

>pip freeze は、あなたのvirtualenvにインストール済みの全てのPythonライブラリを一覧にして出力します。そのpip freezeした出力先を、>　の後に示しファイルに保存します。> requirements.txt を含まずに pip freeze だけで実行してみて、何が起こるか試してみるとよいでしょう。

```requirements.txt
#<...>
psycopg2==2.5.4
```

を追加。

次にprocfileを用意。procfileとは

>このファイルが、どのコマンドを実行してウェブサイトをスタートするかHerokuに伝えます。

プロジェクトディレクトリないで`Procfile`を作成して

```Procfile
web: gunicorn mysite.wsgi
```

を追加。

>私たちはweb アプリケーションをデプロイしようとしていること、そしてgunicorn mysite.wsgiというコマンドを実行することでデプロイすることを意味しています。

次に`runtime.txt`を作成して以下を追加。

```runtime.txt
python-3.5.2
```

>コンピューター上のローカルとサーバーでは設定に違いがあります。Herokuとコンピューターでは別のデータベースをそれぞれつかっています。そこで、別途ファイルを作成し、ローカル環境で動かすための設定を保存しておく必要があります。

```project/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'),
    }
}

DEBUG = True
```

続いて`settings.py`も編集

```project/settings.py
DATABASES['default'] = dj_database_url.config()

SECURE_PROXY_SSL_HEADER = ('HTTP_X_FORWARDED_PROTO', 'https')

ALLOWED_HOSTS = ['*']

STATIC_ROOT = 'staticfiles'

DEBUG = False

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

上記を追加。

>このファイルは、Herokuに必要な構成だけでなく、`mysite/local_settings.py`ファイルがある時にはローカルの設定にも重要な役割となります。

```project/wsgi.py
from whitenoise.django import DjangoWhiteNoise
application = DjangoWhiteNoise(application)
```

続いてheroku toolbeltをインストール。詳しいやり方は<a href="https://devcenter.heroku.com/articles/heroku-cli" target="_blank">こちら</a>を参考に。macの方でbrewがインストールされていれば`brew install heroku-toolbelt`をして取れるはずですが、保証はありません。笑

herokuでアカウントを作成した後、`heroku login`をしてheroku内へログイン。

##Git周辺を整えておく

プロジェクトディレクトリ内で`.gitignore`を作成。

```.gitignore
myvenv
__pycache__
staticfiles
local_settings.py
db.sqlite3
*.py[co]
```

プロジェクトディレクトリのトップ階層で`git init`を実行。`git status`で状態を確認しておく。

```
On branch master

Initial commit

Untracked files:
  (use "git add <file>..." to include in what will be committed)

	.gitignore
	Procfile
	bin/
	blog/
	lib/
	manage.py
	pip-selfcheck.json
	pyvenv.cfg
	requirements.txt
	runtime.txt
　　　　 project/
```

続けて`git add -A .`と`git commit -m "first commit"`を実行。

##heroku上でアプリケーションを作成

続いて`heroku create project_name`を実行してheroku上でプロジェクトを作成して`git push heroku master`

![Screen Shot 2016-12-13 at 11.03.39 PM.png](https://qiita-image-store.s3.amazonaws.com/0/87666/613af888-49c8-08e2-f008-9ea2d4684ba3.png)

続いて`heroku ps:scale web=1`を設定して無料で使えるように。。
`Scaling dynos... done, now running web at 1:Free`

そして`heroku open`を実行。するとエラーページが表示される。

>コマンドで、https://project_name.herokuapp.com/といったあなたのアプリケーションのURLがブラウザで開かれます。おそらくエラーページが表示されることでしょう。これは、私たちがまだadmin viewしか作成していないからです。URLの後ろに admin/ をつけてアプリケーションが動いているのをみてみましょう。(例 https://djangogirlsblog.herokuapp.com/admin/)

![Screen Shot 2016-12-13 at 11.08.36 PM.png](https://qiita-image-store.s3.amazonaws.com/0/87666/ac46cf80-5717-d6fd-9574-d6e57a32cdf2.png)


>エラーが表示されたのは、Herokuにデプロイする時に新しいデータベースを作成したため、まだデータベースが空っぽの状態だからです。そこで、プロジェクトの最初にローカルにデータベースをセットアップした時のように、再度migrate コマンドを実行します:

そこで`heroku run python3 manage.py migrate`と`heroku run python manage.py createsuperuser`を実行。任意の情報を入力してデプロイ完了！ページ編集など細かいところでやることはまだまだあるがとりあえずデプロイが終ったのでめでたしめでたし。まだページはNot foundのままですがそこからの工程はまた後日追記致します。

###ルーティング（追記）

続けて今回はルーティングを行っていこうと思う。

```project/urls.py
from django.conf.urls import include, url
from django.contrib import admin

urlpatterns = [
    url(r'^admin/', include(admin.site.urls)),
    url(r'', include('blog.urls')),
]
```

```blog/urls.py
from django.conf.urls import include, url
from . import views

urlpatterns = [
    url(r'^$', views.post_list),
]
```

`r'^$'`は何もないURL（ここでは`127.0.0.1:8000`を指している）ので`127.0.0.1:8000`に飛んできたユーザーを`views.post_list`へ誘導するように設定している。

すると`AttributeError: module 'blog.views' has no attribute 'post_list'`と怒られてしまう。これは`post_list`を定義してくれたのは良いけどそんなものはviewsの中にないよと言っている。

##Viewsの作成

```blog/views.py
from django.shortcuts import render

# Create your views here.

def post_list(request):
    return render(request, 'blog/post_list.html', {})
```

引数としてのrequestが`blog/post_list.html`を受け取って`render`で出力している。

![Screen Shot 2016-12-14 at 10.55.41 PM.png](https://qiita-image-store.s3.amazonaws.com/0/87666/4f0eacc9-1982-d46b-8090-1c35ebdf3368.png)

テンプレートファイルがないと言っている。ということでつくりましょ。

##HTMLテンプレートの作成

形はこんなかんじ

```
blog
└───templates
    └───blog
```

以下を作成

```blog/templates/blog/post_list.html
#中身は空っぽで
```

これで`http://127.0.0.1:8000/`に進むと真っ白のページがでてくるはず。

```blog/templates/blog/post_list.html
<html>
    <p>Hi there!</p>
    <p>it works!</p> 

</html>
```

で再度接続してみると、

![Screen Shot 2016-12-14 at 11.20.42 PM.png](https://qiita-image-store.s3.amazonaws.com/0/87666/2312f977-236b-7a0b-c956-bbfe73c3c611.png)

htmlの簡単な紹介は上記のサイトの内容を引用します。以下Django Girls Tutorialより引用。

![Screen Shot 2016-12-14 at 11.23.45 PM.png](https://qiita-image-store.s3.amazonaws.com/0/87666/7dbeb3a5-bd18-07a7-f43a-3f3a541c89a8.png)

次に

```blog/templates/blog/post_list.html
<html>
    <head>
        <title>ryugakuotaku</title>
    </head>
    <body>
        <div>
            <h1><a href="">ryugakuotaku</a></h1>
        </div>

        <div>
            <p>published: 14.06.2014, 12:14</p>
            <h2><a href="">My first post</a></h2>
            <p>test</p>
        </div>

        <div>
            <p>published: 14.06.2014, 12:14</p>
            <h2><a href="">My second post</a></h2>
            <p>test</p>
        </div>
    </body>
</html>
```

を試してみる。

キリの良いところまできたので`git status`と`git add -A .`でどこか変わったのかを確かめる。

```
	new file:   blog/templates/blog/post_list.html
	new file:   blog/urls.py
	modified:   blog/views.py
	modified:   ryugakuotaku/urls.py
```

続いて`git commit -m "Changed the HTML for the site."`して`git push heroku master`行う。これでherokuへの処理が終われば正常に反映されているはず。

```
Counting objects: 10, done.
Delta compression using up to 4 threads.
Compressing objects: 100% (8/8), done.
Writing objects: 100% (10/10), 1.04 KiB | 0 bytes/s, done.
Total 10 (delta 4), reused 0 (delta 0)
remote: Compressing source files... done.
remote: Building source:
remote: 
remote: -----> Python app detected
remote:      $ pip install -r requirements.txt
remote: 
remote:      $ python manage.py collectstatic --noinput
remote:        61 static files copied to '/app/staticfiles'.
remote: 
remote: -----> Discovering process types
remote:        Procfile declares types -> web
remote: 
remote: -----> Compressing...
remote:        Done: 66.8M
remote: -----> Launching...
remote:        Released v5
remote:        https://ryugakuotaku.herokuapp.com/ deployed to Heroku
remote: 
remote: Verifying deploy... done.
```

とりまここまででhtmlにhello worldを出力させるまで。