はじめに
フロントにAngular、バックエンドでDjangoを採用した時、Apacheに載せるのにちょっと苦労したのでそのまとめです。
流れ
そもそもこの流れで正しいのかって話ですが、
- クライアントがApacheにリクエストを投げる
- APIへのリクエストかそれ以外へのリクエストかを振り分ける
- APIへのリクエストなら対応するViewを実行してレスポンスを返す
- 静的ファイルへのリクエストは普通に返す
- それ以外へのリクエストならtemplateを返す
このような形です。
設定
以下の順で行います。
- Apacheとpythonの連携
- DjangoとAngularの連携
OSはLinux、pythonは3系の想定です。
Apacheとpythonの連携
mod_wsgiのインストール
公式のインストール方法に則ってインストール
mod_wsgiとは
Apache上でWSGI(Web Server Gateway Interface)を満たすPythonウェブアプリケーションを動かすためのモジュール。
プロジェクトの作成
django-admin startproject sample
cd sample
python3 manage.py startapp sampleapp
httpd.confの追加
Apacheの設定ファイルに追加をします。
httpd.confを直接編集しても良いのですが、httpd.conf内の
Include /private/..../*.conf
のように書いてあるフォルダに新規設定ファイルを追加すれば、設定が追加されます。
# mod_wsgiのロード
LoadModule wsgi_module <pythonのsite-packagesまでのパス>/mod_wsgi/server/mod_wsgi-py36.cpython-36m-darwin.so
# リクエストの振り分け
# staticへのリクエストは直接
Alias /static <プロジェクトのパス>/static
<Directory <プロジェクトのパス>/static>
Require all granted
</Directory>
# それ以外のリクエストはwsgi.pyへ流す
WSGIScriptAlias / <プロジェクトのパス>/sample/wsgi.py
# Pythonモジュールがどこにあるかの指定
WSGIPythonPath <pythonのsite-packagesまでのパス>
# プロジェクト以下のファイルに対する設定
<Directory <プロジェクトのパス>>
Order deny,allow
Require all granted
</Directory>
念のため、httpd -t コマンドでチェックしてください。
挙動確認
ここまでで、ApacheへリクエストするとDjangoからレスポンスが返ってくるはずなので、サンプルでリクエストを返すようにして確認します。
# デフォルトからの差分のみ表示
# application = get_wsgi_application()
def application(environ, start_response):
status = '200 OK'
output = b'Hello World!'
response_headers = [('Content-type', 'text/plain'),
('Content-Length', str(len(output)))]
start_response(status, response_headers)
return [output]
この状態でApacheにアクセスして、レスポンスとして'Hello World'が表示されたらOKです。
確認が済んだら変更分を元に戻し、プロジェクトをモジュールとして実行させるために以下のように編集します。
# デフォルトからの差分のみ表示
import sys
path = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
if path not in sys.path:
sys.path.append(path)
DjangoとAngularの連携
Angularプロジェクトの作成
ng new angular-sample
Djangoの設定編集
プロジェクトディレクトリ配下にtemplateフォルダとstaticフォルダを作成します。
setting.pyを以下のように編集します。
# 差分のみ
INSTALLED_APPS = [
'sampleapp'
]
TEMPLATES = [
{
# templateを探す場所を指定する
'DIRS': [os.path.join(BASE_DIR, 'template')],
},
]
# 静的ファイルの場所を指定
STATICFILES_DIRS = [
os.path.join(BASE_DIR, 'static')
]
url.pyの編集
from django.contrib import admin
from django.urls import path, include, re_path
from django.views.generic.base import TemplateView
urlpatterns = [
path('admin/', admin.site.urls),
# API呼び出しはsampleappの想定。
# path('sampleapp/', include('sampleapp.urls')),
# 上記以外のurlならテンプレートを返す
re_path(r'.*', TemplateView.as_view(template_name='index.html'), name='home')
]
Angularプロジェクトのコンパイルとindex.htmlの修正
ng build --output-hashing=none
オプション(prod, output-hashing)は公式を確認して、ふさわしいものを選択します。
.angular-cli.jsonをいじっていなかったら、distフォルダにコンパイルされたプロジェクトができています。
その中からindex.htmlを先ほど作成したtemplateフォルダに、それ以外をstaticフォルダ配下に移動させます。
一度index.htmlを編集してしまえば、buildするときにstaticフォルダを指定すると楽です。
index.html内のパス(src属性等)を、Djangoのテンプレートエンジンを使用する方法に修正します。
{% load static %}
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>AngularSample</title>
<base href="/">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" type="image/x-icon" href="{% static 'favicon.ico' %}">
</head>
<body>
<app-root></app-root>
<script type="text/javascript" src="{% static 'inline.bundle.js' %}"></script>
<script type="text/javascript" src="{% static 'polyfills.bundle.js' %}"></script>
<script type="text/javascript" src="{% static 'styles.bundle.js' %}"></script>
<script type="text/javascript" src="{% static 'vendor.bundle.js' %}"></script>
<script type="text/javascript" src="{% static 'main.bundle.js' %}"></script>
</body>
</html>
この状態でApacheへ適当なリクエストをすると、Angularのホーム画面が見れるはずです。
終わりに
とりあえず動くようにはできていると思いますが、本番環境で静的ファイルをどのように管理するか等の課題は残ります。
不足や不備、アドバイス等があれば指摘をお願いします。
参考
https://www.codingforentrepreneurs.com/projects/angular-django/
https://docs.djangoproject.com/ja/2.1/howto/deployment/wsgi/
http://httpd.apache.org/docs/2.4/mod/core.html
https://docs.djangoproject.com/ja/2.1/ref/settings/