前回、Python + Django + HighchartsのHerokuアプリ作成を記事にしました。
今回は、これからのDjangoで開発するときの参考にするため、その時の流れや考えたことを残しておきます。
目次
- 開発環境
- 仕様
- tweets
- api
- highcharts
- ディレクトリ構成
- 開発環境のセットアップ
- アプリ共通ライブラリの作成
- tweetsアプリの作成
- apiアプリの作成
- highchartsアプリの作成
- Herokuへデプロイするための準備
- Herokuへデプロイ
- ソースコード
開発環境
- Windows7 x64
- IntelliJ IDEA 14.1.4
- Python 3.4.3
- Django 1.8.4
- PostgreSQL 9.4.4 x64
仕様
アプリ構成
1つのDjangoプロジェクトを3つのアプリ(tweets, api, highcharts)で構成することにしました。それぞれのアプリの機能は、以下の通りです。
tweets
- Twitter APIを利用してツイートを収集し、テーブルへ登録
- Heroku Scheduleで一日一回実行するため、manage.pyで実行可能な形にする
- プロジェクト全体で使うModelはこのアプリに置いた
api
- tweetsアプリのModelからデータを読み出し、JSONとして返す
highcharts
ディレクトリ構成
以下を参考して、主なディレクトリ構成は以下の通りとしました。
Django Best Practiceへの道 #1
<project_root>\ ├── apps\ │ ├── api\ │ │ ├── urls.py │ │ └── views.py │ ├── highcharts\ │ │ ├── templates\ │ │ │ ├── base.jinja2 │ │ │ ├── total.jinja2 │ │ │ └── total_by_month.jinja2 │ │ ├── urls.py │ │ └── views.py │ └── tweets\ │ ├── management\commands\ │ │ └── gather_tweets.py │ ├── migrations\ │ ├── urls.py │ └── views.py ├── dj_ringo_tabetter\ │ ├── settings.py │ ├── urls.py │ └── wsgi.py ├── libs\ │ └── cultivars.py ├── static\ │ └── js\ │ ├── total.js │ └── total_by_month.js ├── .env ├── Procfile ├── apples.yaml ├── manage.py ├── requirements.txt └── runtime.txt
開発環境のセットアップ
PostgreSQLをセットアップ
インストール
現時点では、Heroku PostgresはPostgreSQL 9.4系のようでした。
Heroku Postgres | Heroku Dev Center
そこで、同じバージョンのPostgreSQLとして、Win x86-64版(postgresql-9.4.4-3-windows-x64.exe
)をダウンロード・インストールします。
インストール時の設定は以下の通りとします。また、スタックビルダのインストールは行いません。
項目 | 値 |
---|---|
Installation Directory | C:\PostgreSQL\9.4 |
Data Directory | C:\PostgreSQL\9.4\data |
postgres user password | postgres |
port | 5432 |
Locale | C |
PostgreSQLにデータベースを作成
以下のチュートリアルを参考に、pgAdmin3でデータベースの上で右クリックし、新しいデータベースを作成します。なお、開発環境だったので、UserやRoleの新規作成は特にしませんでした。
python - How to get postgresSQL to work on a windows 7 computer with django? - Stack Overflow
設定内容は以下の通りです。
項目 | 値 |
---|---|
名前 | ringo_tabetter_py |
オーナー | postgres |
Djangoプロジェクト作成
IntelliJ IDEAにて、Djangoプロジェクトを作成します。自分の場合は作成時にエラーが出たため、以前の記事の方法で回避しました。
IntelliJ IDEAにて、virtualenv + Djangoプロジェクトの新規作成時にエラー - メモ的な思考的な
なお、virtualenv環境はプロジェクトと同じフォルダを指定し、作成時の設定は以下の通りとしました。
項目 | 値 |
---|---|
Addtional Libraries and Frameworks | Django |
Template language | Jinja2 |
Template folder | template |
Application name | 空白のまま |
Enable Django admin | チェックする |
Project name | dj_ringo_tabetter |
IntelliJ IDEAの設定変更
デフォルトでは、ファイルの文字コードがwindows-13j
となっているので、utf-8
へと変更しました。
- File > Settings > Editor > File Encodings
- 反映されない場合、IntelliJ IDEAを再起動
Djangoプロジェクトのデータベース設定変更
DjangoではSQLite3がデフォルトのようですが、今回はPostgreSQLを使うため、以下の設定変更を行います。
Pythonパッケージpsycopg2
のインストール
PythonからPostgreSQLへと接続するため、以前の記事を参考に、psycopg2をvirtualenv環境にeasy_installでインストールしました。
(dj_ringo_tabetter) path\to\dj_ringo_tabetter\Scripts>easy_install path\to\psycopg2-2.6.1.win32-py3.4-pg9.4.4-release.exe ... Finished processing dependencies for psycopg2==2.6.1
Pythonパッケージdj-database-url
のインストール
今回はHeroku postgresを使用するため、接続するのに便利なPythonパッケージdj-database-url
をインストールします。
- kennethreitz/dj-database-url · GitHub
- Heroku Database Settings Injection - How do I setup my dev django database? - Stack Overflow
データベースまわりの設定変更
まずは、接続に関する設定として、sqlite3用からPostgreSQL用に<project_root>\dj_ringo_tabetter\settings.py
を修正します。
Settings | Django documentation | Django
なお、開発環境のPostgreSQLへ接続してみたところ、引数engine
を渡さないとエラーが発生しました。似たような事例は以下にありました。
postgresql - Django Heroku database error ENGINE not provided - Stack Overflow
そのため、両環境を識別するような環境変数を探したところ、環境変数DYNO
を使うのが良さそうでした。
- python - How can I detect Heroku's environment? - Stack Overflow
- Dynos and the Dyno Manager | Heroku Dev Center
それを元にsettings.pyのDATABASES
に関する設定を変更します。
次に、日付項目をタイムゾーン付で保存するために、USE_TZ = True
となっていることを確認します。
Time zones | Django documentation | Django
Djangoのタイムゾーンと日本語化の設定
dj_ringo_tabetter\settings.py
を修正します。
# LANGUAGE_CODE = 'en-us' LANGUAGE_CODE = 'ja' # TIME_ZONE = 'UTC' TIME_ZONE = 'Asia/Tokyo'
ここまでのアプリ全体は以下の通りです。
thinkAmi/dj_ringo_tabetter at 7ba235e805ca7243d58b2d44503da7d23cf70b5c · GitHub
アプリ共通ライブラリの作成
今回はアプリ共通ライブラリとして、
という機能を持ったcultivars.py
を作成します。
パスは<project_root>\libs\cultivars.py
としました。
PythonパッケージPyYAML
のインストール
YAMLファイルを読み込むために、PyYAML
をインストールします。
品種などを含むYAMLファイルを作成
以下の内容を持つ、<project_root>\apples.yaml
ファイルを作成します。
- Name: あいかの香り Color: Crimson - Name: 秋映 Color: DarkRed # 以下略
cultivars.pyの実装
YAMLファイルの読み方はこんな感じで、settings.pyに記載されているBASE_PATHを利用してYAMLのフルパスを取得します。
ここまでのアプリは、以下のコミットになります。
thinkAmi/dj_ringo_tabetter at 2667c2af0e0c4cdcde83ce634c264354521974c1 · GitHub
tweetsアプリの作成
3つのアプリのうち、まずはツイートを収集するtweets
アプリから作成します。
また、このアプリの中にModelを作成し、データベースへと保存できるようにします。
Pythonパッケージdjango_jinja
のインストール
manage.pyを使用してtweetsアプリを作成しようと、 Tools > Run manage.py Task...
を実行しようとしたところ、
ImportError: No module named 'django_jinja'
というエラーが発生しました。IntelliJ IDEAでの作成時にINSTALLED_APPS
に記載されるものの、パッケージdjango_jinja
はインストールされないようでした。
Django1.8からはJinja2をテンプレートエンジンとして使えるようですが、今回はdjango_jinja
パッケージをインストールすることにします。
Django1.8からテンプレートエンジンにJinja2を選べるようになっていた #djangoja - Qiita
manage.pyによる、tweets
アプリの生成
再度、Tools > Run manage.py Task...
を実行したところ、manage.pyウィンドウが表示されます。
アプリを作成するために、
manage.py@dj_ringo_tabetter > startapp tweets ./apps/tweets
を実行したところ、以下のエラーが発生しました。
CommandError: Destination directory 'path\to\project\apps\tweets' does not exist, please create it first.
エラーメッセージや以下の情報を見ると、事前にフォルダを作成しておく必要がありそうでした。
django-admin and manage.py | Django documentation | Django
そのため、<project_root>\apps\tweets
フォルダを作成してから、再実行したところ、tweetsアプリが生成されました。
settings.pyへアプリの登録
Djangoにアプリを認識させるために、<project_root>\dj_ringo_tabetter\settings.py
を修正します。
なお、アプリ名にはフォルダ名(apps
)も忘れずに付けます。
INSTALLED_APPS = ( # 略 'django_jinja', 'apps.tweets', #追加 )
Modelの追加
<project_root>\apps\tweets\models.py
にTweets
とLastSearch
という、こんな感じの2つのModelを追加します。
manage.pyによるマイグレーションファイルの作成
Modelを追加したため、manage.pyにてマイグレーションファイルを作成します。INSTALLED_APPSと異なり、apps
の指定は不要です。
manage.py@dj_ringo_tabetter > makemigrations tweets ... Migrations for 'tweets': 0001_initial.py: - Create model LastSearches - Create model Tweets
manage.pyによるマイグレーション
マイグレーションファイルができたところで、引き続きmanage.pyを使ってPostgreSQLのマイグレーションを行います。
manage.py@dj_ringo_tabetter > migrate ... Applying sessions.0001_initial... OK Applying tweets.0001_initial... OK
manage.pyで実行可能なコマンドの作成
Twitter APIを使ってツイートを収集する機能については、manage.pyで実行できるようにしたいことから、以下を参考にして作成します。
- Writing custom django-admin commands | Django documentation | Django
- 紹介マニアどらふと版: Django の機能をコマンドラインやcronから実行する手順
今回はgather_tweets
というコマンドを作成したいことから、<project_root>\apps\tweets\management\commands\gather_tweets.py
というファイルを作成します。
必要なPythonパッケージのインストール
tweepy
PythonでTwitter APIを扱うパッケージを探したところ、以下の機能を持つtweepy
があったため、インストールします。
- Application authに対応
- Cursorを使うことで、ページとかを意識しなくて済む
tweepyの使い方については以下が参考になりました。
Tweepyの2.3.0が出ました - Shogo's Blog
django-dotenv
Twitter APIのApplication authでは、Consumer KeyとConsumer Secretを使います。
ただ、ソースコード上にハードコードしたり、バージョン管理ファイルとして保存したくないことから、.env
ファイルを使うことにします。
Djangoの場合、django-dotenv
を使って.envファイルから環境変数へロードします。
jpadilla/django-dotenv · GitHub
django-dotenvを使うために、<project_root>\dj_ringo_tabetter\settings.py
に以下の行を追記します。
import dotenv dotenv.read_dotenv(os.path.join(BASE_DIR, '.env'))
pytz
TwitterのStatusのcreated_atはタイムゾーンを持っていません。ただ、そのままでは扱いづらいことから、タイムゾーンを持たせて日本時間へと変換できるようにします。
そのため、pytz
をインストールし、タイムゾーンを持てるようにします。
- Pythonの日付処理とTimeZone | Nekoya Press
- pytz - World Timezone Definitions for Python ? pytz 2015.2 documentation
gather_tweets.pyの実装
実装の大枠は以下の通りとなります。
from django.core.management.base import BaseCommand class Command(BaseCommand): def handle(self, *args, **options): # ....
Twitterのパラメータについては以下が参考になりました。
また、Modelを経由したデータベースの更新では、以下を参考にtransaction.atomic()
を使ったトランザクションを実行しています。
Database transactions | Django documentation | Django
manage.pyによる、gather_tweetsのテスト実行
ここまでで、Twitter APIで取得したツイートをPostgreSQLに登録する機能ができました。
そのため、manage.pyにて実際の動作をテストします。
manage.py@dj_ringo_tabetter > gather_tweets ... # 成功した場合、以下の表示あり commit finish
なお、データベースの中身をすべてクリアしたい場合、flush
コマンドを実行します。
manage.py@dj_ringo_tabetter > flush ... Are you sure you want to do this? Type 'yes' to continue, or 'no' to cancel: yes Installed 0 object(s) from 0 fixture(s) Installed 0 object(s) from 0 fixture(s) Installed 0 object(s) from 0 fixture(s)
tweetsアプリ作成後のコミットは以下になります。
thinkAmi/dj_ringo_tabetter at 6f441f39bbc9f41ba186b206d839905b0fe62b74 · GitHub
apiアプリの作成
一般的なapiアプリのため、メモは簡単にしておきます。
apiアプリの実装
- 事前準備
<project_root>\apps\api
フォルダを作成
- アプリの作成
- manage.pyにて、
startapp api ./apps/api
を実行
- manage.pyにて、
- Djangoへアプリの登録
<project_root>\ringo-tabetter-py\settings.py
のINSTALLED_APPSへapps.api
を追加
- URLの設定
<project_root>\apps\api\urls.py
を実装<project_root>\ringo-tabetter-py\urls.py
へ追加
- Viewの作成
<project_root>\apps\api\views.py
を実装
apiアプリのテスト
IntelliJ IDEAでプロジェクトを起動した後、
へとアクセスして両方ともJSONが返ってくればOKです。
apiアプリ作成後のコミットは以下になります。
thinkAmi/dj_ringo_tabetter at c31a1f62f9008be71437751aece4401efc15a944 · GitHub
highchartsアプリの作成
highchartsアプリの実装
- 事前準備
<project_root>\apps\highcharts
フォルダを作成
- アプリの作成
- manage.pyにて、
startapp highcharts ./apps/highcharts
を実行
- manage.pyにて、
- Djangoへアプリの登録
<project_root>\ringo-tabetter-py\settings.py
のINSTALLED_APPSへapps.highcharts
を追加
- URLの設定
<project_root>\apps\highcharts\urls.py
を実装<project_root>\ringo-tabetter-py\urls.py
へ追加
- Viewの作成
<project_root>\apps\highcharts\views.py
を実装
JavaScriptファイルなどの静的ファイルを用意
静的ファイル用のフォルダを用意
CSSは特に使用しませんので、<project_root>\static\js
というJavaScript用のフォルダだけ準備します。
静的ファイル用フォルダとして認識するよう設定
<project_root>\ringo-tabetter-py\settings.py
に、以下の設定を追加します。
STATICFILES_DIRS = (
os.path.join(BASE_DIR, 'static'),
)
JavaScriptファイルを用意
<project_root>\static\js
の下に、以下の2ファイルを用意します。
- total.js
- total_by_month.js
Jinja2テンプレートの作成
以下を参考に、django-jinjaパッケージを使って、Jinja2テンプレートをviewとして返すようにします。
DEFAULT_JINJA2_TEMPLATE_EXTENSIONを設定
IntelliJ IDEAの場合、拡張子を.jinja2
としておくとJinja2テンプレートとして認識してくれます。
そのため、<project_root>\ringo-tabetter-py\settings.py
に、以下の設定を追加します。
DEFAULT_JINJA2_TEMPLATE_EXTENSION = '.jinja2'
テンプレートエンジンの変更
DjangoのテンプレートエンジンからJinja2へと変更します。
そのため、<project_root>\ringo-tabetter-py\settings.py
に、以下の設定を修正します。
TEMPLATES = [ { "BACKEND": "django_jinja.backend.Jinja2", "APP_DIRS": True, "OPTIONS": { "match_extension": ".jinja2", } }, ]
Jinja2テンプレートの作成
今回作成するJinja2テンプレートはhighchartsアプリ内でのみ使用するため、<project_root>\apps\highcharts\templates\
に作成します。
今回はテンプレート継承なども行うため、
- base.jinja2
- total.jinja2
- total_by_month.jinja2
の3ファイルを作成します。
highchartsアプリのテスト
Djangoアプリを起動し、
にてグラフが表示されればOKです。
highchartsアプリ作成後のアプリ全体は、以下の通りです。
thinkAmi/dj_ringo_tabetter at 1a53828ed18b06aca14d4ee8f6c14232bd4778b1 · GitHub
Herokuへデプロイするための準備
以下を参考に、Herokuへデプロイするための準備を行います。
Rubyに負けるな!HerokuでPython(Django)動かす方法 - Qiita
heroku-toolbeltのインストール
デプロイするためにheroku-toolbeltをインストールします。
Heroku Toolbelt
dj-toolbeltの検討
現時点では、dj-toolbeltをWindowsへインストールするのは厳しそうです。
windows とherokuとpython3.4(64bit)でdjango-toolbeltインストールする時に地獄を見ないために… - Qiita
そのため、dj-toolbeltの内容を確認しますが、
パッケージ | 今回のプロジェクトの状況 |
---|---|
Django | 既にインストール済 |
Gunicorn | 後述のようにして自分で入れる |
dj-database-url | 既にインストール済 |
dj-static | 今回必要なさそう |
だったため、今回はdj-toolbelt自体のインストールは不要そうでした。
procfileの作成
Herokuではgunicorndで動作させるため、<project_root>\Procfile
を以下の内容で作成します。
web: gunicorn --env DJANGO_SETTINGS_MODULE=dj_ringo_tabetter.settings dj_ringo_tabetter.wsgi --log-file -
staticファイルの配信のため、PythonパッケージWhitenoise
をインストール
デフォルトのままのDjangoの場合、Heroku上ではviewは配信してくれますが、total.jsなどの静的ファイルは配信してくれませんでした。
そのため、Heroku公式でも推奨されているPythonパッケージWhitenoise
をインストール・設定をします。
Django and Static Assets | Heroku Dev Center
Whitenoiseの設定として、
<project_root>\dj_ringo_tabetter\settings.py
への設定STATIC_ROOT
、STATICFILES_STORAGE
の設定を、上記のHerokuの記載通りに追加
<project_root>\dj_ringo_tabetter\wsgi.py
への設定- whitenoiseのREADMEに書かれた設定をそのまま追加
を行います。
gunicornを使うための設定
以下を参考に、Herokuでgunicornで動作させる設定を行います。
- Deploying Python Applications with Gunicorn | Heroku Dev Center
- Running Gunicorn — Gunicorn 19.3.0 documentation
ただ、開発環境のWindowsでは動作しません。
そのため、<project_root>\dj_ringo_tabetter\settings.py
のINSTALLED_APPS
へ以下を追加します。なお、INSTALLED_APPSはtupleなので末尾カンマを忘れずに付けます。
# 開発環境がWindowsでgunicornが使えないことから、 # herokuのみgunicornを使えるように設定 if 'DYNO' in os.environ: INSTALLED_APPS += ('gunicorn',)
ちなみに、将来的にはWindowsでの動作も期待が持てそうです。
python - Does Gunicorn run on Windows - Stack Overflow
runtime.txtの用意
Heroku上でのPythonのランタイムを指定するために用意します。
python-3.4.3
requirement.txtの用意
コマンドプロンプトでactivateし、pip freeze
を実行します。
(dj_ringo_tabetter) path\to\dj_ringo_tabetter>pip freeze > requirements.txt
その後、作成されたrequirements.txtに不足している、gunicorn==19.3.0
を追加します。
ここまでの設定が完了後、再度 http://localhost:8000/hc/total などにアクセスして、問題なく動作することを確認します。
Herokuで使うための設定をした後のコミットは以下になります。
thinkAmi/dj_ringo_tabetter at e72e28464d3c62569e55981626e5c1e0d376048b · GitHub
Herokuへデプロイ
.gitignoreの作成
https://www.gitignore.io/api/python,django,intellij,pycharm にて作成したものに、今回のアプリで不要なものを追加します。
.env Scripts/ pyvenv.cfg
gitでコミット
git init ~ git commitまで行います。
Herokuアプリの作成
heroku create
とgit push heroku master
を実行します。
path\to\dj_ringo_tabetter>heroku create dj-ringo-tabetter ... Git remote heroku added path\to\dj_ringo_tabetter>git push heroku master ... To https://git.heroku.com/dj-ringo-tabetter.git * [new branch] master -> master
環境変数の作成
.evnに記載していた環境変数を、heroku上に作成します。
Configuration and Config Vars | Heroku Dev Center
> heroku config:set USER_ID=<user_id> TWITTER_CONSUMER_KEY=<your_key> TWITTER_CONSUMER_SECRET=<your_secret>
Python packageの確認
heroku run pip list
を実行し、Python packageがインストールされているかを確認します。
DBのマイグレーション
> heroku run python manage.py migrate... Applying tweets.0001_initial... OK
動作確認
データ投入
本来ならHeroku Schedulerで実行するものですが、動作確認のために実行します。
> heroku run python manage.py gather_tweets
pgAdminIII で確認
該当するデータベースの、スキーマの下にDjangoで使っているテーブルが居るので、そのテーブルを確認してデータが入っていることを確認します。
HerokuのPostgreSQLをpgAdminから見る方法 - MD Blog
herokuアプリを起動して、動作を確認
heroku open
で開いて、
http://<your_application_name>/api/v1/total
http://<your_application_name>/hc/total
などで動作を確認します。
データ削除
先ほど投入したものは不要なデータなので、削除しておきます。
> heroku run python manage.py flush
スケジュールアドオンの追加
ツイートを収集する部分は一日一回動作するようスケジュールします。今回もHerokuのアドオン、Heroku Scheduler
を使います。
設定した内容は以下の通りです。なお、最初はEvery 10 minutesにしてみて、動作するかを確認しておきます。
項目 | 値 |
---|---|
$ | python manage.py gather_tweets |
FREQUENCY | Daily |
Next DUE | 18:00 (JSTで3:00) |
Heroku Postgresのデータ移行やアプリ名の変更については、前回の記事通りですので、省略します。
以上で、dj-ringo-tabetterを作った時の流れは終わりです。
ソースコード
再掲となりますが、最新のソースコードは以下となります。
thinkAmi/dj_ringo_tabetter · GitHub