概要
db.relationship で外部キーの参照ができます
外部キーとして紐付いている先から参照元のデータを取得したケースがあると思います
今回はその方法を紹介します
環境
- macOS 11.6.1
- Python 3.8.3
準備
- pipenv install Flask-Migrate Flask-SQLAlchemy Flask-Marshmallow Flask mysqlclient
my_app/database.py
Item は必ずどれかの User に紐付いています
User は Item に紐付いていないケースがあります
item = ma.Nested(ItemSchema, many=True)
で参照元を参照します
item の部分の変数名は item = db.relationship("Item", backref="user")
で使用している変数名と同じにする必要があります
同じにしないと SqlAlchemy が参照元を辿れず値がうまく取得できません
from flask_sqlalchemy import SQLAlchemy
from flask_marshmallow import Marshmallow
db = SQLAlchemy()
ma = Marshmallow()
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(128))
age = db.Column(db.Integer)
__table_args__ = (
db.UniqueConstraint('name', 'age', name='unique_name_age'),
)
item = db.relationship("Item", backref="user")
def __repr__(self):
return "%s,%s,%i" % (self.id, self.name, self.age)
class Item(db.Model):
id = db.Column(db.Integer, primary_key=True)
user_id = db.Column(db.Integer, db.ForeignKey("user.id"))
name = db.Column(db.String(20))
class ItemSchema(ma.Schema):
class Meta:
fields = ("id", "name")
class UserSchema(ma.Schema):
class Meta:
fields = ("id", "name", "age", "item")
item = ma.Nested(ItemSchema, many=True)
my_app/__init__.py
User の情報を取得するためのアプリです
from flask import Flask, jsonify
from flask_migrate import Migrate
from my_app.database import db, ma
from my_app.lib import UserCRUD
app = Flask(__name__)
app.debug = True
app.config['SQLALCHEMY_DATABASE_URI'] = "mysql+mysqldb://root:@localhost/test?charset=utf8"
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = True
db.init_app(app)
migrate = Migrate(app, db)
ma.init_app(app)
@app.route('/<id>')
def select(id=None):
crud = UserCRUD()
user = crud.select(id)
return jsonify(user)
my_app/lib/__init__.py
User テーブルを操作するためのクラスです
from my_app.database import db, User, UserSchema
class UserCRUD():
def select(self, id):
user = User.query.get(id)
return UserSchema(many=False).dump(user)
DB マイグレート
-
mysql -u root -p -e "create database test;"
- FLASK_APP=my_app pipenv run flask db init
- FLASK_APP=my_app pipenv run flask db migrate -m “Create item table”
- FLASK_APP=my_app pipenv run flask db upgrade
insert into user values (1, 'hawk', 10);
insert into user values (2, 'snowlog', 20);
insert into item values (1, 1, 'apple');
insert into item values (2, 1, 'grape');
insert into item values (3, 1, 'banana');
動作確認
- FLASK_APP=my_app pipenv run flask run
- curl localhost:5000/1
{"age":10,"id":1,"item":[{"id":1,"name":"apple"},{"id":2,"name":"grape"},{"id":3,"name":"banana"}],"name":"hawk"}
- curl localhost:5000/2
{"age":20,"id":2,"item":[],"name":"snowlog"}
こんな感じで User から Item を参照することができます
Item クラスの特定のフィールドだけ返したい場合
post_dump を使うといいかなと思います
from flask_sqlalchemy import SQLAlchemy
from flask_marshmallow import Marshmallow
from marshmallow import post_dump
db = SQLAlchemy()
ma = Marshmallow()
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
name = db.Column(db.String(128))
age = db.Column(db.Integer)
__table_args__ = (
db.UniqueConstraint('name', 'age', name='unique_name_age'),
)
item = db.relationship("Item", backref="user")
def __repr__(self):
return "%s,%s,%i" % (self.id, self.name, self.age)
class Item(db.Model):
id = db.Column(db.Integer, primary_key=True)
user_id = db.Column(db.Integer, db.ForeignKey("user.id"))
name = db.Column(db.String(20))
class ItemSchema(ma.Schema):
class Meta:
fields = ("id", "name")
class UserSchema(ma.Schema):
class Meta:
fields = ("id", "name", "age", "item")
item = ma.Nested(ItemSchema, many=True)
best_item_name = ma.String(default="")
@post_dump
def set_best_item(self, data, many, **kwargs):
if data["item"]:
data["best_item_name"] = data["item"][0]["name"]
del data["item"]
return data
最後に
User.query.get(id)
の結果から item を参照することはできるのですが Schema で逆参照を使う場合に少し工夫が必要です
0 件のコメント:
コメントを投稿