【Django Rest Framework】アプリ毎にDB接続先を切り替える構成について(SimpleJWT)
概要
Djangoにて以下画像のように作成するアプリ毎に対するDB設定を切り替え各設定先のDBやスキーマを見にいく(読込/書込)データベースの設定を切り替えれないかなと思い調べた結果、
表題にある通りの「Django Rest Framework」と「SimpleJwt」を使用する構成であれば少なくともログインやDB読み込み/書き込みの際の接続先設定を切り替えられそうなことが判明しましたので設定するべき項目等を紹介できればと思います。
https://gyazo.com/6cccd5c4340ade1d202bc36400f0964e
設定する項目
まず概要にある構成を実現しようと思った際に設定するべき項目というのは、主に以下5件となります。
おそらく以下を一通り設定すればアプリ毎にDB設定を切り替えられるようになるかと思います。
■設定項目
・DATABASESでの各種DB接続先の設定(seteigs.py)
・設定したDATABASESに対するアプリ毎のマッピング設定(settigs.py etc...)
・(connection.cursor()を使用している場合)生SQLの実行先コネクション切替設定(app各処理)
・JWTAuthentication設定のカスタマイズ(JWTAuthentication)
・TokenObtainPairView等のトークン発行/検証/リフレッシュ関連のカスタマイズ(TokenObtainPairView etc...)
①DATABASESでの各種DB接続先の設定
アプリ毎で設定するDatabase設定を以下のように書きます。
後でアプリとのマッピングを設定するので、設定名はなんでも良いです
code: (edit)settings.py
DATABASES = {
# app1用
'default': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'db_ap1.sqlite3',
},
# app2用
'app2': {
'ENGINE': 'django.db.backends.sqlite3',
'NAME': BASE_DIR / 'db_app2.sqlite3',
}
}
②設定したDATABASESに対するアプリ毎のマッピング設定
①にて設定したデータベース設定に対してアプリ毎のマッピング設定を設定します。
※config(プロジェクト名)/db_router.pyの追加、config(プロジェクト名)/setting.pyへの追記
以下マッピング用のdb_router.pyの追加とそれを使用するようにsettings.pyへの追記を行います。
code: (new)config/db_router.py
from config.settings import DATABASE_APPS_MAPPING
class DatabaseRouter(object):
def db_for_read(self, model, **hints):
if model._meta.app_label in DATABASE_APPS_MAPPING:
return None
def db_for_write(self, model, **hints):
if model._meta.app_label in DATABASE_APPS_MAPPING:
return None
def allow_relation(self, obj1, obj2, **hints):
db1 = DATABASE_APPS_MAPPING.get(obj1._meta.app_label)
db2 = DATABASE_APPS_MAPPING.get(obj2._meta.app_label)
if db1 and db2:
return db1 == db2
return None
def allow_migrate(self, db, app_label, model=None, **hints):
if db in DATABASE_APPS_MAPPING.values():
return DATABASE_APPS_MAPPING.get(app_label) == db
elif app_label in DATABASE_APPS_MAPPING:
return False
code: (edit)settings.py
DATABASES = {
...
}
# アプリケーション毎の接続先DBマッピング
DATABASE_APPS_MAPPING = {
# DjangoAdmin管理系Table
'admin' : 'default',
'auth' : 'default',
'contenttypes' : 'default',
'sessions' : 'default',
'messages' : 'default',
'staticfiles' : 'default',
# アプリ毎のマッピング設定
'app1' : 'default',
'app2' : 'app2' ,
}
# 利用するRouter, manage.pyから見ての相対パス
DATABASE_ROUTERS = [
'config.db_router.DatabaseRouter',
]
これでORMを使用している場合は、アプリ毎でORMの参照先のDB設定が自動的に切り替わるようになります。
③生SQLの実行先コネクション切替設定(app各処理)
django.dbのconnection.cursor()で生SQLを実行している場合は、①②の設定だけでは読み込み先DBの設定がデフォルトから切り替わらないので、以下のようにアプリ毎にdjango.dbのconnectionsからコネクションの指定をした上でcursorが使われるように更新します。
code:(before)各種アプリのviews.py
from django.db import conection
...
# SQL文の実行
with connection.cursor() as cursor:
cursor.execute(sql, params)
columns = [col0 for col in cursor.description] return queryset
code:(after)各種アプリのviews.py
from django.db import conections
...
# SQL文の実行
with connections'default'.cursor() as cursor: cursor.execute(sql, params)
columns = [col0 for col in cursor.description] return queryset
上記で生SQLを使用している場合でも読み込み先のデータベース設定を切り替えられるようになります。
④JWTAuthentication設定のカスタマイズ
SimpleJWTを使ってトークン検証を組んでいる場合はここの検証時も現状だとパッケージからデフォルトのDBを参照してチェックが行われるので以下のようにアプリ毎のデータベース設定を見てもらうように使用するユーザモデルをアプリ毎のmodelsに切り替えます。
code:(new)各種アプリ毎のJWTAuthentication.py
また、作成したJWTAuthentication.pyをトークン検証時に使ってもらうようにviewsに設定します。
code:各種アプリのviews.py
from rest_framework import permissions
from rest_framework.viewsets import ModelViewSet
# from rest_framework_simplejwt.authentication import JWTAuthentication
from app1.common.JWTAuthentication import JWTAuthentication
from app1.serializer.content import ContentListSerializer
class ContentListViewSet(ModelViewSet):
serializer_class = ContentListSerializer
...
⑤TokenObtainPairView等のトークン発行処理のカスタマイズ
トークン発行時にも基本的にデフォルトのデータベース設定を見にいくような設定となっているので、
ここも④と同じくView/Serialiserをサブクラス化しカスタマイズします。
code: (edit)app1/views/auth/token_obtain_pair.py
参考サイト
・【Django公式】Multiple databases
・【Qiita】django データベースの接続先をログインユーザ別に切り替える
・【Github.io/個人ブログ】Djangoでデータベースを分割して利用する