はじめに
Flaskのmodelクラスを別モジュールに切り出したので、その方法をまとめます。
ベストプラクティスとは思えませんが、今のところ動いてます。
どんな時に使うか
Flaskでアプリをガシガシ作ってきて、あるタイミングでmodelクラスを別のアプリからも使いたいと思った時。
クリーンアーキテクチャのモデルとして使いたいので、データベースの詳細には依存したくないとき。
免責
クリーンアーキテクチャと言いつつ、flaskのSQLAlchemyにおもいっきり依存しているのだが、そこはOKの前提。
方法
1. 別モジュールのためのディレクトリを作成
たとえばこんなかんじ。
shared_models/
|-- shared_models/
| |-- __init__.py
| |-- models.py
|-- tests/
| |-- __init__.py
| |-- test_models.py
|-- setup.py
2. setup.pyを作成
from setuptools import setup, find_packages
setup(
name='shared_models',
version='0.1',
packages=find_packages(),
install_requires=[
# Any dependencies required
],
)
3. models.pyを作成
すでに作成済みのモデルクラスをMyModel
とYourModel
とします。init_models
は、モデルクラスの定義をアプリのライフサイクルでただ一度だけ行います。データベースへの依存を、引数のdb
で注入しています。これにより、shared_models
は外部に依存しないモジュールになります。
_IS_ALREADY_DEFINED = False
def init_models(db):
global _IS_ALREADY_DEFINED
if _IS_ALREADY_DEFINED:
return
_IS_ALREADY_DEFINED = True
global MyModel, YourModel
# すでに定義済みのモデルクラスをここにコピー
class MyModel(db.Model):
# My model definition
pass
class YourModel(db.Model):
# Your model definition
pass
4. コンシューマー側のアプリでshared_modelsを使う
shared_models
を使いたいコンシューマーアプリは、init_models(db)
を呼び出してモデルクラスを定義しておきます。その後は、モデルクラスをインポートできます。
database.py
がこうなってたとします。
from flask_sqlalchemy import SQLAlchemy
db = SQLAlchemy()
# app.py
from database import db
from shared_models.core_models import init_models
init_models(db) # 依存性の注入とモデルクラスの定義
from shared_models.core_models import MyModel, YourModel # モデルクラスのインポート
@app.route('/api/my_api')
def my_api():
my_model = MyModel.query.all()
...
別のファイルからも、まったく同じようにモデルクラスをインポートできます。
init_model
は何度呼び出しても、モデルクラスの定義は一度しか行われません。
# other_file.py
from database import db
from shared_models.core_models import init_models
init_models(db) # 依存性の注入とモデルクラスの定義
from shared_models.core_models import MyModel, YourModel # モデルクラスのインポート
def my_function():
your_model = YourModel.query.all()
...
5. モデルクラスを継承する
shared_modelsで定義したモデルクラスを継承することもできます。
from database import db
from shared_models.core_models import init_models
init_models(db) # 依存性の注入とモデルクラスの定義
from shared_models.core_models import MyModel, YourModel # モデルクラスのインポート
class CustomMyModel(Utterance):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
def say_hello(self):
return f"Hello from Custom Model"
# 通常通りインポートして使える ##############
from app.models import CustomModel
custom_model = CustomMyModel.query.get(1)
print(f"TEST {custom_model}")