DjangoとFlaskを合わせて使う

DjangoとFlaskはそれぞれPythonのWebサービスを作るためのフレームワーク。

Flaskはシンプルで使いやすいけど、DBが絡んでくるとDjangoの便利なAdmin管理画面を使いたくなる。

[こんな記事](https://gist.github.com/seanbehan/168a277a5a23086e76000a59e54fb3d5)を見つけた。普段はFlaskのAPIサーバだけ立ち上げて、DBの中身を触りたい時だけDjangoを立ち上げるという事をやってみたくなった。

# 要件
- Ubuntu

# 環境構築
```
$ sudo apt install virtualenv
$ virtualenv env -p python3
$ source env/bin/activate
$ pip install django flask
```

# Django構築
```
$ django-admin startproject djangoflask
$ ./manage.py migrate
$ ./manage.py createsuperuser
```

起動確認。以下のコマンドでテストサーバを立ち上げて`http://127.0.0.1:8000/admin`で管理画面にアクセスできるか。

```
$ ./manage.py runserver
```

アプリケーションの追加。

```
$ django-admin startapp flaskapp
```

```python:djangoflask/djangoflask.py
INSTALLED_APPS = [
    ...
    'flaskapp',
]

```

適当にモデルを作ってみる例。

```python:flaskapp/models.py
from django.db import models

class Post(models.Model):
    title = models.CharField(max_length=100, default='')
    body  = models.TextField(max_length=500, default='')

    def __str__(self):
        return self.title
```

```
$ ./manage.py makemigrations flaskapp
$ ./manage.py migrate
```

```python:flaskapp/admin.py
from django.contrib import admin
from .models import Post

@admin.register(Post)
class PostAdmin(admin.ModelAdmin):
    pass
```

管理画面にアクセスしてPostを編集出来るか。

# Flask構築
Djangoのモデルクラスが使えるように先頭でごにょごにょする。

```python:flaskapp/flask.py
import os
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "djangoflask.settings")
from django.apps import apps
from django.conf import settings
apps.populate(settings.INSTALLED_APPS)

from flask import Flask, jsonify
from .models import Post

def create_app():
    app = Flask(__name__)
    return app

app = create_app()

@app.route('/')
def index():
    return "THIS IS FLASK INDEX PAGE."

@app.route('/<int:post_id>')
def post(post_id):
    post = Post.objects.filter(id=post_id).values().first()
    return jsonify(post)
```

`$ ./manage.py runserver`でDjangoのテストサーバが立ち上がるので、`$ ./manage.py flask`でFlaskのテストサーバが立ち上がるようにしてみる。

```python:manage.py
#!/usr/bin/env python
import os
import sys

def exec_django():
    try:
        from django.core.management import execute_from_command_line
    except ImportError as exc:
        raise ImportError(
            "Couldn't import Django. Are you sure it's installed and "
            "available on your PYTHONPATH environment variable? Did you "
            "forget to activate a virtual environment?"
        ) from exc
    execute_from_command_line(sys.argv)

if __name__ == "__main__":
    os.environ.setdefault("DJANGO_SETTINGS_MODULE", "djangoflask.settings")
    if len(sys.argv) > 1 and sys.argv[1] == "flask":
        from flaskapp.flask import app
        app.run()
    else:
        exec_django()
```

Flaskのテストサーバを起動して`http://127.0.0.1:5000/1`でPostデータにアクセス出来るか。

```
$ ./manage.py flask
```

違うターミナルでDjangoのテストサーバも立ち上げて異なるポートでDB管理画面を開きながらAPIサーバにアクセスできるか。

# 本番サーバ（nginx+uWSGI）の準備

本番サーバの公開にはnginxとuWSGIを使う。

```
$ sudo apt install nginx
$ pip install uwsgi
```

もしuWSGIのインストールで`Exception: you need a C compiler to build uWSGI`と言われた場合は、

```
$ sudo apt install build-essential
```

もしuWSGIのインストールで`fatal error: Python.h: そのようなファイルやディレクトリはありません`と言われた場合は、

```
sudo apt install python3-dev
```

起動ファイルの作成。

```ini:django_uwsgi.ini
[uwsgi]
module = djangoflask.wsgi:application
master = true
processes = 1
socket = /tmp/django_uwsgi.sock
chmod-socket = 666
vacuum = true
die-on-term = true
```

```ini:flask_uwsgi.ini
[uwsgi]
module = flaskapp.flask:app
master = true
processes = 1
socket = /tmp/flask_uwsgi.sock
chmod-socket = 666
vacuum = true
die-on-term = true
```

設定の追加。

```python:djangoflask/settings.py
...
STATIC_URL = '/static/'
STATIC_ROOT = os.path.join(BASE_DIR, 'static')
```

nginxの設定。

```conf:/etc/nginx/conf.d/djangoflask.conf
server {
  listen 80;
  listen [::]:80;
  server_name example.wakin.site;

  location / {
    include uwsgi_params;
    uwsgi_pass unix:///tmp/flask_uwsgi.sock;
  }

  location /admin {
    include uwsgi_params;
    uwsgi_pass unix:///tmp/django_uwsgi.sock;
  }
  location /static/ {
    alias /path/to/djangoflask/static/;
  }
}
```

nginxの再起動

```
$ sudo systemctl restart nginx
```

各本番サーバを起動してアクセスしてみる。

```
$ uwsgi --ini django_uwsgi.ini
```

```
$ uwsgi --ini flask_uwsgi.ini
```

アクセス出来れば、ひとまずタイトルの題目は完了。

# supervisorでデーモン化

本番サーバの起動をデーモン化しておく。

```
$ sudo apt install supervisor
```

設定ファイルの作成。

```conf:/etc/supervisor/conf.d/django.conf
[program:django]  ; プロセスの名前
directory=/path/to/djangoflask  ; 作業ディレクトリ
command=/path/to/env/bin/uwsgi --ini django_uwsgi.ini ; 起動コマンド
numprocs=1  ; 起動インスタンス数？
autostart=true  ; autostartする
autorestart=true  ; 自動で再起動させる
user=***  ; 起動ユーザ
redirect_stderr=true  ; 標準エラーを出力する
stdout_logfile=/var/log/supervisor/django.log  ; ログファイルの出力先
```

```conf:/etc/supervisor/conf.d/flask.conf
[program:flask]  ; プロセスの名前
directory=/path/tp/djangoflask ; 作業ディレクトリ
command=/path/to/env/bin/uwsgi --ini flask_uwsgi.ini ; 起動コマンド
numprocs=1  ; 起動インスタンス数？
autostart=true  ; autostartする
autorestart=true  ; 自動で再起動させる
user=***  ; 起動ユーザ
redirect_stderr=true  ; 標準エラーを出力する
stdout_logfile=/var/log/supervisor/flask.log  ; ログファイルの出力先
```

設定ファイルの再読み込み。

```
$ sudo supervisorctl reload
```

プロセスの確認。

```
$ sudo supervisorctl status
```

止めるときは、

```
$ sudo supervisorctl stop <プロセス名>
```

立ち上げるときは、

```
$ sudo supervisorctl start <プロセス名>
```

アクセスログ等は`/var/log/supervisor/*.log`に書き込まれる。

普段は`django`は止めておき、DBの管理画面を開きたい時だけ立ち上げる運用が出来る。