フラスコの休息:複雑なオブジェクトを JSON にマーシャリングする
-
21-12-2019 - |
質問
FlaskのRestful拡張機能について質問があります。使い始めたばかりですが、1つの問題に直面しました。私は持っている flask-sqlalchemy
多対 1 の関係に接続されているエンティティがあり、その RESTful エンドポイントがすべての子を含む親エンティティを返したいと考えています。 json
マーシャラーを使って。私の場合、Set には多くのパラメータが含まれています。フラスコをゆっくりと眺めました ドキュメント しかし、この事件を解決する方法については何も説明されていませんでした。
明らかな何かが欠けているようですが、解決策がわかりません。これが私のコードです:
# entities
class Set(db.Model):
id = db.Column("id", db.Integer, db.Sequence("set_id_seq"), primary_key=True)
title = db.Column("title", db.String(256))
parameters = db.relationship("Parameters", backref="set", cascade="all")
class Parameters(db.Model):
id = db.Column("id", db.Integer, db.Sequence("parameter_id_seq"), primary_key=True)
flag = db.Column("flag", db.String(256))
value = db.Column("value", db.String(256))
set_id = db.Column("set_id", db.Integer, db.ForeignKey("set.id"))
# marshallers
from flask.ext.restful import fields
parameter_marshaller = {
"flag": fields.String,
"value": fields.String
}
set_marshaller = {
'id': fields.String,
'title': fields.String,
'parameters': fields.List(fields.Nested(parameter_marshaller))
}
# endpoint
class SetApi(Resource):
@marshal_with(marshallers.set_marshaller)
def get(self, set_id):
entity = Set.query.get(set_id)
return entity
restful_api = Api(app)
restful_api.add_resource(SetApi, "/api/set/<int:set_id>")
今、私が電話すると /api/set/1
サーバーエラーが発生します:
TypeError: 'Set' object is unsubscriptable
したがって、エンドポイントがこの JSON を返すように set_marshaller を正しく定義する方法が必要です。
{
"id": : "1",
"title": "any-title",
"parameters": [
{"flag": "any-flag", "value": "any-value" },
{"flag": "any-flag", "value": "any-value" },
.....
]
}
助けていただければ幸いです。
解決
私はその問題の解決策を自分で見つけました。
いろいろ遊んだ後は flask-restful
いくつかの間違いを犯したことがわかりました:
まず最初に set_marshaller
次のようになります:
blob_marshaller = {
'id': fields.String,
'title': fields.String,
'parameters': fields.Nested(parameter_marshaller)
}
パラメータがリストであり、マーシャリングされる場合、Restless マーシャラーは大文字と小文字を処理できます。 json
リスト。
もう1つの問題は、API Setパラメータに遅延読み込みがあるため、Setをマーシャルしようとすると、 KeyError: 'parameters'
, したがって、次のようにパラメータを明示的にロードする必要があります。
class SetApi(Resource):
@marshal_with(marshallers.set_marshaller)
def get(self, set_id):
entity = Set.query.get(set_id)
entity.parameters # loads parameters from db
return entity
または、別のオプションは、モデルの関係を変更することです。
parameters = db.relationship("Parameters", backref="set", cascade="all" lazy="joined")
他のヒント
私は Flask-RESTful を使用しています。これは、ネストされたプロパティの読み込みに対する解決策です。
呼び出し可能オブジェクトをマーシャル デコレータに渡すことができます。
class OrgsController(Resource):
@marshal_with(Organization.__json__())
def get(self):
return g.user.member.orgs
次に、モデルを更新して、独自のエンティティのリソース フィールドを返します。したがって、ネストされたエンティティは、そのエンティティのリソース フィールドを相対的に返します。
class Organization(db.Model):
id = db.Column(db.Integer, primary_key=True)
...
@staticmethod
def __json__(group=None):
_json = {
'id': fields.String,
'login': fields.String,
'description': fields.String,
'avatar_url': fields.String,
'paid': fields.Boolean,
}
if group == 'flat':
return _json
from app.models import Repository
_json['repos'] = fields.Nested(Repository.__json__('flat'))
return _json
class Repository(db.Model):
id = db.Column(db.Integer, primary_key=True)
owner_id = db.Column(db.Integer, db.ForeignKey('organization.id'))
owner = db.relationship('Organization', lazy='select', backref=db.backref('repos', lazy='select'), foreign_keys=[owner_id])
...
@staticmethod
def __json__(group=None):
_json = {
'id': fields.String,
'name': fields.String,
'updated_at': fields.DateTime(dt_format='iso8601'),
}
if group == 'flat':
return _json
from app.models import Organization
_json['owner'] = fields.Nested(Organization.__json__('flat'))
return _json
これにより、私が探している表現が得られ、遅延読み込みが尊重されます。
[
{
"avatar_url": "https://avatars.githubusercontent.com/u/18945?v=3",
"description": "lorem ipsum.",
"id": "1805",
"login": "foobar",
"paid": false,
"repos":
[
{
"id": "9813",
"name": "barbaz",
"updated_at": "2014-01-23T13:51:30"
},
{
"id": "12860",
"name": "bazbar",
"updated_at": "2015-04-17T11:06:36"
}
]
}
]
好き
1) このアプローチにより、エンティティごとにリソース フィールドを定義できるようになり、アプリ全体のすべてのリソース ルートで利用できるようになります。
2) グループ引数を使用して表現を希望通りにカスタマイズできる方法。ここでは「フラット」のみを使用していますが、任意のロジックを記述して、より深いネストされたオブジェクトに渡すことができます。
3) エンティティは必要な場合にのみロードされます。