djangoは誰が動かしているのか？（デプロイのための俯瞰）

#概要
django flameworkでの開発中にサーバー関連で何度もスタックしまして、同様に嵌る人が多かったので、そもそもどのようにdjangoがWebサービスを提供しているのかという切り口で俯瞰してみようと思いました。

なお、この記事はdjangoのモジュールの中身について解説するものではありません。あくまでサーバー周りの話です。

#記事の目的・まとめ

djangoのデプロイ手順を覚えるのは大変なので、まずはざっくりと全体像を掴んでスタックしたときにどこを調べたらよいか把握するための記事です。一応、個別のファイル設定についても記載していますが細かい部分は最新のドキュメントを参照ください。

図でまとめるとこんな感じです。
![django_driver_structure.png](https://qiita-image-store.s3.amazonaws.com/0/271466/c3cd426f-0ebf-1f9d-03f2-023f5efa1b8e.png)

ここから細かい話&実際の手順になります。何の話をしてるのかわからなくなったら図に戻ってください。

#前置き：djangoの動き方は1つではない
djangoの動かし方は開発時とリリース時で異なります。

**開発時**：manage.py のrunserver を使用します。8000番ポートでmanege.pyがlistenして動きます。

**リリース時**：Apacheがリクエストをlistenした後、pythonを動かすことのできる別のモジュールにバトンを渡して動かします。（この記事ではモジュールとしてmod_wsgiを使っています。）

djangoの公式チュートリアルはmanage.pyを使った開発サーバで動かす方法を記載しており、実際にリリースする際には切り替え作業が発生します。

#第一走者：Apache
Apacheは設定を変更していなければ80番ポートをlistenしています。
80番ポートからリクエストを受け取ると、次の走者にバトンを渡すために設定ファイルを参照します。
設定ファイルは通常/etc/httpd/conf/httpd.confにあります。

/etc/httpd/conf/httpd.confに直接書いてもいいですが、こちらは基本的な設定のみ記載し
プロジェクトごとに別途/etc/httpd/conf.d/[project].confを作って細かな設定を別に記載する方法が一般的です。

```:httpd/conf/httpd.conf
# /etc/httpd/conf/httpd.confから/etc/httpd/conf.d/[project].confを参照します
ServerRoot "/etc/httpd"
Include conf.modules.d/*.conf
```

```:httpd/conf.d/[project].conf
LoadModule wsgi_module path/to/pythonhome/lib/python3.6/site-packages/mod_wsgi/server/mod_wsgi-py36.cpython-36m-x86_64-linux-gnu.so
WSGISocketPrefix /var/run/wsgi
```

Apacheは/etc/httpd/conf.d/[project].confからmod_wsgiの場所を確認し、mod_wsgiにバトンを渡します。

#第二走者：mod_wsgi
mod_wsgiはApacheからの呼び出しを受けてpythonとwsgi.pyを探します。
mod_wsgiもまたApacheの設定ファイルを利用します。
wsgi.pyは通常[project]/[project]/wsgi.pyの位置に自動で作られます。


```:httpd/conf.d/[project].conf
WSGIPythonHome /path/to/pythonhome/ #pythonのインストールディレクトリです。
WSGIPythonPath /path/to/projecthome/ 

# httpで/[project]をリクエストされたときwsgi.pyにアクセス
WSGIScriptAlias /[project] /path/to/projecthome/[project]/wsgi.py 

# wsgi.pyへのアクセスを許可
<Directory /path/to/projecthome/[project]>
  <Files wsgi.py>
    Require all granted
  </Files>
</Directory>
```


※Pythonの場所は $which python で探せます。

#第三走者：wsgi.py
wsgi.pyはmod_wsgiによって呼び出されるとsetting.pyから環境変数を受け取ってDjangoを動かします。
wsgi.pyの役割はrunserver中のmanage.pyと対応しています。


```Python:wsgi.py
site.addsitedir("/path/to/pythonhome/lib/python3.6/site-packages")
sys.path.append("/path/to/projecthome/")
sys.path.append("/path/to/projecthome/[project]")
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "[project].settings")
```

```Python:settings.py
WSGI_APPLICATION = '[project].wsgi.application' #これを受け取ってdjangoのモジュールを動かします
```

```Python:wsgi.py
from django.core.wsgi import get_wsgi_application
application = get_wsgi_application()
```

#そういえば
この流れに従って設定を終えたところで、データベースってどうなってましたっけ？

組み込みのsqlLiteならそのまま動きますが、MySQLサーバーを使っている場合Djangoからでは直接動かせないので、manage.pyからpymysqlなどクライアントのモジュールを呼ぶようにしていたはずです。manage.pyに対応するwsgi.pyにもこの記述をしてやることでMySQLとの接続が正常に行えるようになります。

```Python:wsgi.py
import pymysql
pymysql.install_as_MySQLdb()
```
※ここで新たにMySQLを使う方はsettings.pyにもMySQL用の記述をお忘れなく。

#これで動く…のか？
開発中は正常に表示されていた画像やCSSが一時的に表示されなくなります。
これはmanage.pyで動かしていた時とwsgi.pyで動かしている時で{%load static%}の参照先が変わる為です。

wsgi.pyはmanage.pyのように静的ファイルを自ら探しに行かずApacheに探させます。
Apacheがdjangoの実行ファイルが入っているディレクトリを動き回るのを避けるため、djangoは(1)「別途ディレクトリを作って静的ファイルをコピー」し(2)「Apacheにコピーした静的ファイルの参照先を教える」といった手順を採用しています。

ファイルのコピーはmanage.pyにまとめて行わせることができます。
manege.pyがコピーする際に使う環境変数をsettings.pyに設定します。

```Python:settings.py
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__))) 
# settings.pyの絶対パスを取って、親の親をBASE_DIRにしている
# settings.pyの親の親 = /path/to/projecthome/

STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR, 'static/') # = /path/to/projecthome/static
STATICFILES_DIRS = (
    os.path.join(BASE_DIR, '[app]/static/'), # 開発中に使っていたstaticディレクトリ
)
```

settings.pyを更新したら
$python manage.py collectstatic
をします。すると静的ファイルがsettings.pyの記述に従って /path/to/projecthome/static/ にコピーされます。

今度はApacheに静的ファイルの場所を教えます。/etc/httpd/conf.d/<project>.confを開き

```:httpd/conf.d/[project].conf
Alias /static/ /path/to/projecthome/static/ # collectstaticでコピーしたstaticディレクトリのパスを登録

# staticへのアクセスを許可
<Directory /path/to/projecthome/static>
  Require all granted
</Directory>
```
で完了です。

---
以上。