Frasco reparador:marshal objeto complejo a json
-
21-12-2019 - |
Pregunta
Tengo una pregunta sobre la extensión de descanso del matraz.Recién comencé a usarlo y enfrenté un problema.Tengo flask-sqlalchemy
entidades que están conectadas en una relación de muchos a uno y quiero que ese punto final tranquilo devuelva la entidad principal con todos sus hijos en json
usando marshall.En mi caso, Set contiene muchos parámetros.Miré el matraz-reposado documentos pero no hubo ninguna explicación de cómo resolver este caso.
Parece que me falta algo obvio pero no puedo encontrar ninguna solución.Aquí está mi código:
# 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>")
Ahora cuando llamo /api/set/1
Recibo un error del servidor:
TypeError: 'Set' object is unsubscriptable
Entonces necesito una manera de definir correctamente set_marshaller y ese punto final devuelve este json:
{
"id": : "1",
"title": "any-title",
"parameters": [
{"flag": "any-flag", "value": "any-value" },
{"flag": "any-flag", "value": "any-value" },
.....
]
}
Agradezco cualquier ayuda.
Solución
Yo mismo encontré la solución a ese problema.
Después de jugar con flask-restful
Descubrí que cometí algunos errores:
En primer lugar set_marshaller
debería verse así:
blob_marshaller = {
'id': fields.String,
'title': fields.String,
'parameters': fields.Nested(parameter_marshaller)
}
El marshaller inquieto puede manejar el caso si el parámetro es una lista y dirigirse a json
lista.
Otro problema fue que en API Set los parámetros tienen una carga diferida, por lo que cuando intento ordenar Set obtengo KeyError: 'parameters'
, entonces necesito cargar explícitamente parámetros como este:
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
U otra opción es cambiar la relación del modelo:
parameters = db.relationship("Parameters", backref="set", cascade="all" lazy="joined")
Otros consejos
Esta es una adición a Zygimantas's respuesta:
Estoy usando Flask-RESTful y esta es una solución para la carga de propiedades anidadas.
Puede pasar un invocable al decorador mariscal:
class OrgsController(Resource):
@marshal_with(Organization.__json__())
def get(self):
return g.user.member.orgs
Luego actualice los modelos para devolver los campos de recursos de su propia entidad.Por lo tanto, las entidades anidadas devolverán relativamente los campos de recursos de su entidad.
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
Esto proporciona la representación que estoy buscando y respeta la carga diferida:
[
{
"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"
}
]
}
]
Me gusta
1) cómo este enfoque me permite definir mis campos de recursos por entidad y está disponible para todas mis rutas de recursos en la aplicación.
2) cómo el argumento del grupo me permite personalizar la representación como desee.Aquí solo tengo "plano", pero cualquier lógica se puede escribir y transmitir a objetos anidados más profundos.
3) las entidades solo se cargan según sea necesario.