Flask-restful: marshal complex object to json
Solution 1
I found solution to that problem myself.
After playing around with flask-restful
i find out that i made few mistakes:
Firstly set_marshaller
should look like this:
set_marshaller = {
'id': fields.String,
'title': fields.String,
'parameters': fields.Nested(parameter_marshaller)
}
Restless marshaller can handle case if parameter is list and marshals to json
list.
Another problem was that in API Set parameters has lazy loading, so when i try to marshall Set i got KeyError: 'parameters'
, so I need explicitly load parameters like this:
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
Or another option is to change model relationship:
parameters = db.relationship("Parameters", backref="set", cascade="all" lazy="joined")
Solution 2
This is an addition to Zygimantas's answer:
I'm using Flask-RESTful and this is a solution to the loading of the nested properties.
You can pass a callable to the marshal decorator:
class OrgsController(Resource):
@marshal_with(Organization.__json__())
def get(self):
return g.user.member.orgs
Then update the models to return the resource fields for its own entity. Nested entities will thus return the resource fields for its entity relatively.
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
This gives the representation I'm looking for, and honoring the lazy loading:
[
{
"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"
}
]
}
]
I like
1) how this approach allows me to define my resource fields per entity and it is available to all my resource routes across the app.
2) how the group argument allows me to customise the representation however I desire. I only have 'flat' here, but any logic can be written and passed down to deeper nested objects.
3) entities are only loaded as necessary.
Related videos on Youtube
Zygimantas Gatelis
Updated on June 26, 2022Comments
-
Zygimantas Gatelis almost 2 years
I have a question regarding flask restful extension. I'm just started to use it and faced one problem. I have
flask-sqlalchemy
entities that are connected many-to-one relation and I want that restful endpoint return parent entity with all its children injson
using marshaller. In my case Set contains many parameters. I looked at flask-restful docs but there wasn't any explanation how to solve this case.Seems like I'm missing something obvious but cannot figure out any solution. Here is my 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>")
Now when i call
/api/set/1
I get server error:TypeError: 'Set' object is unsubscriptable
So I need a way to correctly define set_marshaller that endpoint return this json:
{ "id": : "1", "title": "any-title", "parameters": [ {"flag": "any-flag", "value": "any-value" }, {"flag": "any-flag", "value": "any-value" }, ..... ] }
I appreciate any help.
-
Atif Shafi over 2 yearsyou saved me pains