まずはプロジェクトを作成します。その後そのプロジェクトをスタートラインに立たせるべく、最低限の構成を行います。ここでの最低限とは動作に必要最低限というよりも、この後開発していくのに必要最低限という意味合いの方が強いです。
説明に使っている環境はmacOSでDBにはMySQLを使っています。また開発するアプリの固有名称に”Houjin”というのが登場します。これは現在開発中のアプリで、途中から紹介する例をそのアプリに合流させる魂胆なので、見かけたらご自身の開発するアプリ名称に読み替えて参照下さい。
STEP 1 モジュールのインストール
export BASE_NAME="LearningDjango" export BASE_DIR="${HOME}/git/${BASE_NAME}" python -m venv ${BASE_NAME} cd ${BASE_NAME} source bin/activate pip install pipenv pip install --upgrade pip pipenv install django gunicorn mysqlclient
この辺りの流れについては、こちらに記載の内容に全く同じです。実施内容をまとめると以下の通り。
- venvでLearningDjangoというプロジェクトを作成
- 作ったプロジェクト環境をactivate
- pipenvをpipでインストール
- pipを最新に
- django、gunicorn、mysqlclientをpipenvでインストール
【サマリー】 新規仮想環境の作り方
キーワード: 新規 venv 仮想環境 作り方
python -m venv ${BASE_NAME}
STEP 2 Djangoプロジェクト作成と構成
続いてDjangoプロジェクトを作成します。このとき以外あまり登場機会がありませんがdjango-adminコマンドを使用します。
cd ${BASE_DIR} export PROJECT_NAME="houjin" django-admin startproject ${PROJECT_NAME}
このコマンドは実行してもなんの結果出力もないので全く手応えがありません。今回の場合、houjinというディレクトリが作成されて、その中もう一つ同名のディレクトリとmanage.pyが作成されます。余談ですが、venvのところでhoujinと指定してしまうと3兄弟になってしまいます。
(LearningDjango) MacBookPro:houjin $ tree . ├── houjin │ ├── __init__.py │ ├── asgi.py │ ├── settings.py │ ├── urls.py │ └── wsgi.py └── manage.py
【サマリー】 新規Djangoプロジェクトの作り方
キーワード: 新規 Django プロジェクト 作り方
django-admin startproject ${PROJECT_NAME}
解説: manage.py
Djangoでは作成したプロジェクトで何をするにもこのmanage.pyスクリプトを入口にします。現時点においてmanage.pyの内容で大事な部分は、以下のhoujim.settingsがセットされている部分です。
もし正確な意味が分からないとしても、この行がDJANGO_SETTNGS_MODULES環境変数のデフォルト値を与えているというのは推測できると思います。
この行は、後に登場するhoujin/settings.pyファイルを指すように記載されています。
デフォルト値を与えているということは、自身でこの環境変数にセットしておけば、例えばsettings_dev.pyに切り替えて動作させるといったことも可能ですし、この行をhoujin.settings_devと書き換えることでも同じ事が起こる構図です。
ですが、この行を書いた人の意図としては、何の指定も無ければhoujin/settings.pyが使われ、事前に環境変数に指定があればそれに従うというものですので基本的にはそれに従うと良いでしょう。
設定ファイルを切り替えるには、DJANGO_SETTNGS_MODULES環境変数が使えます。
【サマリー】環境毎にsettings.pyを切り替えたい
キーワード: 環境毎 settings.py 切り替え
export DJANGO_SETTNGS_MODULES="${PROJECT_NAME}/settings_dev.py"
解説: houjin/settings.py
おそらく一度には全て説明し尽くすことが出来ませんし、そもそも私も把握していない事も多々あろうかと思います。ですが、一番最初に気にとめておくこと、設定しておくべき事は網羅したいと思います。
まずは、デバッグモードからです。これがTrueなのかFalseなのかはかなり動作が違ってきます。最も大きな違いはエラー発生時の挙動です。ブラウザからアクセスしエラーとなった場合、ブラウザ上にエラーの詳細が表示されます。エラーそのものだけで無く、その時点での様々な変数の値、つまりはコンテキスト自体がすべてさらけ出されてしまいます。エラーを解決するにはとても有益な情報であると同時にセキュリティ的には最弱の状態と言えます。
つまりコメントにも書かされているように、開発環境でのみTrueとし、本番環境ではFalseとなっているべきものです。
とはいえ、SECRET_KEYがその代表的な例ですが、一部のキーワードを含んでいる設定値はDEBUG=Trueでも表示されない仕様となっています。KEY、PASS、TOKENなどが当てはまるようです。
さきほどのmanage.pyのところで、環境毎にsettings.pyを用意して切り替え可能ということはお分かりいただけたかと思います。ですが、冷静に考えると様々な値が開発と本番では異なっており、それらを別々の名称(たとえばsettings_dev.pyとsettings_prod.py)で作成したとして、特にパスワードなどを念頭にそれらをGit等のリポジトリ管理にするのは少し躊躇があると思います。
できれば、プロジェクト管理外(Git管理外という意味ですね)のところで環境変数としてセットされていることを期待し、プログラム側では環境変数から受領するというのが真っ先に思い浮かぶ方法です。そうなると今度はsettings.pyは環境毎に用意する必要が無くなります。
例えばDEBUGなら、具体的には以下のようにします。
すこし余計な関数が絡んでいますが、要するにHOUJIN_DEBUG=”True”という環境変数が事前にセットされている中で動作する事でDEBUGの値にTrueをセットしようという物です。
ちなみにですが、DEBUG = bool(os.environ[“HOUJIN_DEBUG”])とすることも可能ですが、環境変数にFalseをセットしても、boot()は空の文字列以外でTrueを返却するので、DEBUG = Trueとなってしまい期待通りの動作をしません。
もう一つ付け加えると、strtobool()は所定の文字列以外はValueErrorを返すため、間違えた設定ではアプリが起動しないということを意味するので、気がつかないうちに実はDEBUG=Trueだったなんてことは起こりにくいということも良い点です。
続いては、接続許可ホストのリストです。
上記は何でもOKを意味していますが、このまま本番にリリースされてしまう危険性を考えるとあまりおすすめ出来ません。
設定値としては、本番環境のホスト名を設定しておくのはもちろんのことですが、お名前.comあたりで1円でドメインを取得できたりするので、開発用にそういったものを利用するのを一案です。つまり実際にドメインを取得してそれを自分のマシンで使ってしまうということです。自動延長で1年後に2〜3千円という出費に繋がりかねないので自動更新はoffにしつつ、ありがたく1年間限定で開発用ドメインを使うという作戦です
【早い者勝ち!】.com/.net/.jp ドメイン取るならお名前.com
そもそもドメイン名が変わっても変わったなりに動かないと、開発→本番に持って行けないということを意味するので、1年後に開発用のドメインが変わってもたいした問題は無いでしょう。
データベース接続設定です。
今回はMySQL用の設定です。すべて環境変数から渡されるようにしています。os.environなのでもしも環境変数が無かった場合、KeyErrorが発生してサーバーが起動しません。
他にもos.environ.get()やos.getenv()などのキーが無くてもエラーにならない、無かった場合のデフォルト値を与えられるといった便利そうなものもあるのですが、往々にしてデフォルト値は開発用の値で、それがもとでデバッグモードになっていたという間違いを防ぐ意味でも、個人的にはKeyErrorになる方をお薦めします。
また環境切り替えという意味ではDATABASESと複数系になっていて、設定値もdefaultとラベルがついているのを見ればお分かりの通り、stagingやproductionなど環境別に複数の定義を行うことも可能です。
【サマリー】DBの設定
キーワード: DB 設定値 環境毎 切り替え
DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME': os.environ["HOUJIN_DB_NAME"], 'USER': os.environ["HOUJIN_DB_USER"], 'PASSWORD': os.environ["HOUJIN_DB_PASSWORD"], 'HOST': os.environ["HOUJIN_DB_SERVER"], 'PORT': '3306', } }
すこし脱線しますが、私はこれらはすべてプロジェクト管理外のファイルとして1つにまとめています。具体的には以下のような形です。(bashを使っているのでその例です)
ファイル名: .houjin_env
HOUJIN_ENV="PROD" HOUJIN_DEBUG="False" HOUJIN_DB_SERVER="localhost" HOUJIN_DB_NAME="houjin_prod" HOUJIN_DB_USER="houjin_prod" HOUJIN_DB_PASSWORD="password"
もちろん、これはGit管理外です。なぜこんなことをしているかというと、bashでこれらを有効にしたいのはもちろんのこと、cronやsystectlなど意外とこのアプリに関する環境変数一式が必要となる場面はあります。そんなときこの1ファイルに纏まっていると便利だと思います。
ちなみに、bashで使う時は以下のようにしています。
source ~/.houjin_env export HOUJIN_ENV export HOUJIN_DEBUG export HOUJIN_DB_SERVER export HOUJIN_DB_NAME export HOUJIN_DB_USER export HOUJIN_DB_PASSWORD
さて、本線に戻ります。続いては言語とタイムゾーンの設定です。
お好みで構わないのですが、日本語で東京としています。
STATIC_URLとSTATIC_ROOTの設定です。
STATIC_URLは外部からHTML、CSS、JavaScriptなど、静的なファイルを参照する場合にそのURLの根っこになる部分です。STATIC_ROOTは静的ファイルをどこに配置するかという設定です。
実はSTATIC_ROOTが果たす役割が、本番と開発もっと正確にいえばDEBUG=TrueとFalseで異なり、それがWebサーバーの設定とも関連します。そのあたりは静的ファイルのところで説明したいと思います。
続いてログの構成です。
これについては、下から読んでいった方が理解がし易いかと思います。
まず、ロガーの構成として、appログとdjango.dbログを構成しています。それぞれfileとconsoleの両方に出力するようにしています。ここでのfileやconsoleは名付けただけなので、fileではその名の通りログファイルへ出力し、consoleでは画面出力するように構成しています。そしてそれぞのフォーマットがallとshortというわけです。
1つだけ注意点は、ログディレクトリはこの時点で存在しません。ですのでlogディレクトリを作成して下さい。作成する箇所は、上記の構成の通りだとmanage.pyと同じ階層です。ご自身で出力先の設定を変えられた場合には、それに応じてということになります。
【サマリー】ログの構成
キーワード: ログ 構成 ログファイル コンソール DBログ
LOGGING = { 'version': 1, 'formatters': { 'all': { 'format': '\t'.join([ "%(asctime)s", "[%(levelname)s]", "%(filename)16s:%(lineno)4d:%(funcName)16s", "%(message)s", ]) }, 'short': { 'format': '\t'.join([ "[%(levelname)s]", "%(filename)16s:%(lineno)4d:%(funcName)16s", "%(message)s", ]) }, }, 'handlers': { 'file': { 'level': 'DEBUG', 'class': 'logging.FileHandler', 'filename': os.path.join(BASE_DIR, 'log/django.log'), 'formatter': 'all', }, 'console': { 'level': 'DEBUG', 'class': 'logging.StreamHandler', 'formatter': 'short' }, }, 'loggers': { 'app': { 'handlers': ['file', 'console'], 'level': 'DEBUG', }, 'django.db': { # django also has database level logging 'handlers': ['file', 'console'], 'level': 'DEBUG', 'propagate': False, }, }, }
必要な構成は一通り終わりました。
続いてDBを構成します。migrateは何度となく忘れた頃にお世話になります。しかも今後makemigrationsさんの登場も手伝って、migration? あれ?migrations? もしかしてスペルミス?からのmigrateなんてことも?
python manage.py migrate
このとき、環境変数以外にも明示的にsettings.pyの切り替えを指示することも可能です。
python manage.py migrate --settings=houjin.settings_prod
とすることで、houjin/settings_prod.pyファイルを用いて実行されます。migrateを実行すると以下のようなテーブルが作成されます。
続いて必ずしも必要ではありませんが、管理者ユーザーを作成します。
python manage.py createsuperuser
後ほど動作を確認するためにここで構成した管理者のIDとパスワードを用います。
最後にサーバーを起動して実際に動作しているかを確認してみます。
python manage.py runserver
ブラウザでアクセスしてみます。http://localhost:8000/
続いて管理画面にアクセスしてみます。http://localhost:8000/admin 先ほど作成した管理者ユーザーとそのパスワードを入力し、ログインして下さい。
自作部分はゼロですが、すでにWebアプリケーションとして動作します。これはadminアプリが初めから組み込まれた状態であるためです。
これでようやくスタート地点に立つことが出来ました。
ユーザーとグループが見えていることからも想像できるとおり、管理者以外のユーザーはここから作成可能で、ここで作ったユーザーを今後作成するWebアプリの画面から使ったりも出来ます。また、現時点では管理サイトではユーザーとグループくらいしかなんともなりませんが、ご自身が作成したテーブルもここで扱うことが出来るようになります。そのあたりも今後まとめていきたいと思います。
このあとは、このプロジェクトに自作部分となるアプリを追加して行くことになります。
編集後記
記事の流れ上触れなかったこととして、2つほど。
SECRET_KEY
このキーはとても重要なキーです。Djangoが備えるセキュリティ関連の機能において、暗号化やハッシュなどで使用されていて、DBアクセスのパスワードをsettings.pyに書かないのであれば、同様にSECRET_KEYもそう考えるべきかも知れません。
その場合に気をつけるのは、複数台のサーバーでDjangoを運用する場合、それらのサーバーで共通の値にする必要があります。
環境毎の設定値について
本稿では環境毎の設定値を環境変数に委ねました。この方法は一般的に取られる方法ではあるものの他にも方法はあろうかと思います。たしかそんな機能を提供してくれるパッケージもあったかと思います。
ですが、環境変数にすることはIaaSにPaaSなど様々なクラウドサービス上で稼働させる*こと、そしてインフラ系の機能においても同様の設定値を必要としてそれらと共通化することを考えると、やはり選択肢としては第一候補になり得ます。
*1つのアプリを複数のサービスで稼働させるのではなく、あなたが次に開発するアプリの話です。
もちろん何らかのパッケージを使うことを否定はしませんが、言語が変わるとやり方を変えなければならないので、特にPHP村やRails村などどちらかにお住まいの方は、Django村に住民票を移動させてからでも十分間に合うかと思います。