Flask-SQLAlchemyを使用したAPIを作成していてDBから取得したModelをJSONに変換しようとすると、
SQLAlchemyのModelはそのままJSONに変換できないので何かに詰め替えを行う必要があります。
results = User.query.all() # JSONに変換できずエラーになる return jsonify({'status': 'ok', 'users': results}) |
pythonのmarshmallowというライブラリは、
pythonオブジェクトをJSONに変換したりその逆を行ったりするためのスキーマを定義するためのライブラリです。
今回は、marshmallowを使用してSQLAlchemyのModelをJSONに変換するためのスキーマを定義します。
実装例
from datetime import datetime from flask_sqlalchemy import SQLAlchemy from flask_marshmallow import Marshmallow from flask_marshmallow.fields import fields db = SQLAlchemy() class User(db.Model): id = db.Column(db.Integer, primary_key=True, nullable=False, autoincrement=True) first_name = db.Column(db.String(100), default=None) last_name = db.Column(db.String(100), default=None) created_at = db.Column(db.DateTime, default=datetime.now) updated_at = db.Column(db.DateTime, default=datetime.now, onupdate=datetime.now) ma = Marshmallow() class UserSchema(ma.ModelSchema): class Meta: model = User created_at = fields.DateTime('%Y-%m-%dT%H:%M:%S+09:00') updated_at = fields.DateTime('%Y-%m-%dT%H:%M:%S+09:00') full_name = fields.Method("join_full_name") def join_full_name(self, obj): return '{first_name} {last_name}'.format(last_name=obj.last_name, first_name=obj.first_name) |
使用例
results = User.query.all() return jsonify({'status': 'ok', 'users': UserSchema(many=True, exclude=('first_name', 'last_name')).dump(results).data}) |
出力結果
{ "status": "ok", "users": [ { "id": 1, "full_name": "yuto matsuki", "created_at": "2017-08-19T19:50:39+09:00", "updated_at": "2017-08-19T19:50:39+09:00" }, { ... } ... ] } |
解説
SQLAlchemyでuserテーブルのModelを定義します。
5つのカラム(id,first_name,last_name,created_at,updated_at)を作成します。
db = SQLAlchemy() class User(db.Model): id = db.Column(db.Integer, primary_key=True, nullable=False, autoincrement=True) first_name = db.Column(db.String(100), default=None) last_name = db.Column(db.String(100), default=None) created_at = db.Column(db.DateTime, default=datetime.now) updated_at = db.Column(db.DateTime, default=datetime.now, onupdate=datetime.now) |
marshmallowでModelSchemaを定義します。
SQLAlchemyのModelの場合は以下のようにclass Metaで指定するだけで完了です。
ma = Marshmallow() class UserSchema(ma.ModelSchema): class Meta: model = User |
ここまでを使ってSQLAlchemyで取得したオブジェクトを変換すると
UserSchema().dump(<Userオブジェクト>).data |
以下のようなJSONが得られます。
{ "id": 1, "first_name": "yuto", "last_name": "matsuki", "created_at": "2017-08-19T19:50:39+00:00", "updated_at": "2017-08-19T19:50:39+00:00" } |
ここで問題になるのが、MarshmallowのDateTime型が自動でUTCに変換されてしまう点です。
"2017-08-19T19:50:39+00:00"
そのため、日付フォーマットを指定してあげる必要があります。
以下のように書くことでプロパティを上書きできるようです。
class UserSchema(ma.ModelSchema): class Meta: model = User created_at = fields.DateTime('%Y-%m-%dT%H:%M:%S+09:00') updated_at = fields.DateTime('%Y-%m-%dT%H:%M:%S+09:00') |
また、Modelにないプロパティを追加したい場合もあります。
例えばカスタムフィールドのMethod Fieldsを使用すると
以下のように元のModelを加工したプロパティを追加することができます。
class UserSchema(ma.ModelSchema): class Meta: model = User full_name = fields.Method("join_full_name") def join_full_name(self, obj): return '{first_name} {last_name}'.format(last_name=obj.last_name, first_name=obj.first_name) |
また、Schemaのインスタンス生成時に excludeを使用することで不要なプロパティを削除できます。
UserSchema(exclude=('first_name', 'last_name')).dump(<Userオブジェクト>).data |
以下のようなJSONが得られます。
{ "id": 1, "full_name": "yuto matsuki", "created_at": "2017-08-19T19:50:39+00:00", "updated_at": "2017-08-19T19:50:39+00:00" } |
その他、スキーマの定義を行えばリレーションのあるテーブルを一緒に取って来ることもできたりします。
Marshmallowを知るまではSQLAlchemyのModelからJSON変換用のクラスを作成して詰め替えていたのですが、
単純な詰め替え作業は少ないコードで書けて拡張性もあるので便利かと思います。
この記事を書いた人
最近書いた記事
- 2021.12.10React NativeでWallet風UIを実装する
- 2018.11.19Elasticsearchで簡単な検索とscoreを調整する方法
- 2018.10.05ECSをEC2からFargateに切り替える際の注意点
- 2018.09.12AKB48グループ映像倉庫のWeb版をリリースしました