Flakon-erholsam:Marshallen Sie komplexe Objekte in JSON
-
21-12-2019 - |
Frage
Ich habe eine Frage zur flask restful extension.Ich habe gerade erst angefangen, es zu benutzen, und hatte ein Problem.Ich habe flask-sqlalchemy
Entitäten, die in einer Viele-zu-Eins-Beziehung verbunden sind, und ich möchte, dass dieser erholsame Endpunkt die übergeordnete Entität mit allen ihren untergeordneten Elementen zurückgibt json
mit Marshaller.In meinem Fall enthält Set viele Parameter.Ich schaute flaschenruhevoll zu Dokumente Es gab jedoch keine Erklärung, wie dieser Fall gelöst werden sollte.
Scheint, als würde mir etwas Offensichtliches entgehen, ich kann aber keine Lösung finden.Hier ist mein Code:
# 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>")
Wenn ich jetzt anrufe /api/set/1
Ich erhalte einen Serverfehler:
TypeError: 'Set' object is unsubscriptable
Ich brauche also eine Möglichkeit, set_marshaller korrekt zu definieren, damit der Endpunkt diesen JSON zurückgibt:
{
"id": : "1",
"title": "any-title",
"parameters": [
{"flag": "any-flag", "value": "any-value" },
{"flag": "any-flag", "value": "any-value" },
.....
]
}
Ich bin für jede Hilfe dankbar.
Lösung
Ich habe selbst eine Lösung für dieses Problem gefunden.
Nach dem Herumspielen mit flask-restful
Ich stelle fest, dass ich einige Fehler gemacht habe:
zuerst set_marshaller
sollte so aussehen:
blob_marshaller = {
'id': fields.String,
'title': fields.String,
'parameters': fields.Nested(parameter_marshaller)
}
Restless Marshaller kann die Groß- und Kleinschreibung verarbeiten, wenn der Parameter eine Liste ist und an diese gemarshallt json
Liste.
Ein weiteres Problem bestand darin, dass die API-Set-Parameter verzögert geladen werden. Wenn ich also versuche, Set zu Marshallen, bekomme ich KeyError: 'parameters'
, also muss ich Parameter wie diesen explizit laden:
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
Oder eine andere Möglichkeit besteht darin, die Modellbeziehung zu ändern:
parameters = db.relationship("Parameters", backref="set", cascade="all" lazy="joined")
Andere Tipps
Dies ist eine Ergänzung zu Zygimantas'S Antwort:
Ich verwende Flask-RESTful und dies ist eine Lösung für das Laden der verschachtelten Eigenschaften.
Sie können dem Marshal-Dekorator ein Callable übergeben:
class OrgsController(Resource):
@marshal_with(Organization.__json__())
def get(self):
return g.user.member.orgs
Aktualisieren Sie dann die Modelle, um die Ressourcenfelder für die eigene Entität zurückzugeben.Verschachtelte Entitäten geben daher die Ressourcenfelder für ihre Entität relativ zurück.
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
Dies ergibt die Darstellung, nach der ich suche, und würdigt das verzögerte Laden:
[
{
"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"
}
]
}
]
Ich mag
1) Wie dieser Ansatz es mir ermöglicht, meine Ressourcenfelder pro Entität zu definieren, und wie er für alle meine Ressourcenrouten in der App verfügbar ist.
2) wie das Gruppenargument es mir ermöglicht, die Darstellung nach meinen Wünschen anzupassen.Ich habe hier nur „flach“, aber jede Logik kann geschrieben und an tiefer verschachtelte Objekte weitergegeben werden.
3) Entitäten werden nur bei Bedarf geladen.