Django + Apache with mod_wsgi on Windows Server 2016

#背景
毎回同じところで躓くので自分用にメモ。

#環境
- Windows Server 2016
- Apache 2.4 + mod_wsgi
- Django 3.0

#前提
- Windows Server 2016がインストールされている。
- Apache 2.4がインストールされている。
- 有難い記事 [Python Windows で mod_wsgi を Apache に組み込む方法](https://qiita.com/hatai/items/279ec1dfd28cb9693a1f) の内容は既知とする。

#ダウンロード～インストール
[Python Windows で mod_wsgi を Apache に組み込む方法](https://qiita.com/hatai/items/279ec1dfd28cb9693a1f) に従う。

#mod_wsgiをApacheに組み込む
上記記事の通りではうまくいかなかったので追記。

##1. モジュールのロード＋α
インストールまでできたら、PythonのScriptsディレクトリ（自環境では`C:\Program Files\Python38\Scripts`）に、`mod_wsgi-express.exe`が作成されている。
`mod_wsgi-express module-config`でApache httpd.confの設定が自動生成されるので、これをhttpd.confにコピペする。

```shell
> mod_wsgi-express module-config

LoadFile "c:/program files/python38/python38.dll"
LoadModule wsgi_module "c:/program files/python38/lib/site-packages/mod_wsgi/server/mod_wsgi.cp38-win_amd64.pyd"
WSGIPythonHome "c:/program files/python38"
```
↓

```httpd.conf
…
LoadFile "c:/program files/python38/python38.dll"
LoadModule wsgi_module "c:/program files/python38/lib/site-packages/mod_wsgi/server/mod_wsgi.cp38-win_amd64.pyd"
WSGIPythonHome "c:/program files/python38"
…
```

##2. WSGIScriptAliasの設定
参考：[Django 1.4 documentation - Apache と mod_wsgi 環境で Django を使う方法](http://e-class.center.yuge.ac.jp/jdk_docs/django1.4/howto/deployment/wsgi/modwsgi.html)
>`WSGIScriptAlias`の行の最初の引数は、アプリケーションを公開する場所（`/`はルートURLを表しています）、二つ目の引数はシステム上の「WSGIファイル（後述）」の場所です。 WSGIは通常、プロジェクトパッケージ（この例ではmysite）の中に置きます。これで、ApacheはルートURL以下のすべてのリクエストを、指定のWSGIファイルを使って処理します。

すなわち、第１引数以下に来たリクエストを、第２引数で指定したWSGIファイルで処理する、という意味。第２引数には、接続したいDjangoプロジェクトのアプリの中に入っているwsgi.pyを指定する。

```httpd.conf
WSGIScriptAlias / d:/apps/appname/appname/wsgi.py
```

参考：[mod_wsgi - WSGIScriptAlias](https://modwsgi.readthedocs.io/en/develop/configuration-directives/WSGIScriptAlias.html)

##3. WSGIPythonPathの設定
参考：[Django 1.4 documentation - Apache と mod_wsgi 環境で Django を使う方法](http://e-class.center.yuge.ac.jp/jdk_docs/django1.4/howto/deployment/wsgi/modwsgi.html)
>`WSGIPythonPath`の行は、プロジェクトのパッケージが Python パスでのインポー トで使えることを保証します。

ようは、プロジェクトで使用するsite-packagesの場所を教えてあげるという意味。なお、Pythonのバージョンを変えてvenvをさらに作成した場合は、このパスも変えてあげる必要がある（関連記事：[ValueError at set_wakeup_fd only works in main thread の対処法](https://qiita.com/hiro-jp/items/1a15ac51165f5af395f3)）が、普通は`appname/venv/Lib/site-packages`でよい。

```httpd.conf
WSGIPythonPath d:/apps/appname/venv/Lib/site-packages
```

##4. Directoryの設定
>\<Directory>の部分はApacheがwsgi.pyファイルにアクセスできることを保証します。

下記では、`d:/apps/appname/appname`へのアクセスを許可（`Require all granted`）するという意味。

```httpd.conf
<Directory d:/apps/appname/appname>
   <Files wsgi.py>
       Require all granted
   </Files>
</Directory>
```

**注意**：[Django 1.4 Documentationのような古い資料](http://e-class.center.yuge.ac.jp/jdk_docs/django1.4/howto/deployment/wsgi/modwsgi.html)では下記のような記述になっているが、Apache2.4以降ではこれは動かない（参考：[Apache2.4で、アクセス制限の記述方法が変更されていた](https://qiita.com/nwsoyogi/items/c8eb1fedef3c00c5fbac)）。

```httpd.conf
# Apache2.2以前の記述方法
<Directory d:/apps/appname/appname>
   <Files wsgi.py>
      Order allow,deny
      Allow from all
   </Files>
</Directory>
```

#Django側のwsgi.pyの設定
いざ接続してみると、ここでまさかのInternal Server Error。
![ss19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/117020/ae7459c7-aac9-f4af-c606-b53d907ba979.png)
下記で解決できる。

```python:wsgi.py
"""
WSGI config for appname project.

It exposes the WSGI callable as a module-level variable named ``application``.

For more information on this file, see
https://docs.djangoproject.com/en/3.0/howto/deployment/wsgi/
"""

import os
import sys  # ←追加

from django.core.wsgi import get_wsgi_application

sys.path.append('D:/apps/appname') # ←追加

os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'appname.settings')

application = get_wsgi_application()

```

関連記事：[mod_wsgi 500 Internal Server Errorの対処法](https://qiita.com/hiro-jp/items/03a1718c6a524920de30)

参考：[Apache2 mod_wsgi, 500 Internal Server Error](https://stackoverflow.com/questions/5713689/apache2-mod-wsgi-500-internal-server-error)