The summary might be very confusing but I don't know how to formulate it more concise.
The models I have:
class Movie(db.Model):
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(
imdb_data = db.relationship('IMDBData', uselist=False)
class IMDBData(db.Model):
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(255))
rating = db.Column(db.Float)
movie_id = db.Column(db.Integer, db.ForeignKey('movie.id'))
Using Flask-Restful fields I am marshaling the response like this:
imdb_data_fields = {
'id': fields.Integer,
'title': fields.String,
'rating': fields.Float
}
movie_fields = {
'id': fields.Integer,
'title': fields.String
}
class MovieListAPI(Resource):
def __init__(self):
self.parser = reqparse.RequestParser()
super(MovieListAPI, self).__init__()
def get(self):
self.parser.add_argument('imdb_data', type=str, location='args')
args = self.parser.parse_args()
m_fields = copy.copy(movie_fields)
# TODO: Return empty object if movie.imdb_data = None
if args['imdb_data']:
m_fields['imdb_data'] = fields.Nested(imdb_data_fields)
movies = Movie.query.all()
return {'movies': marshal(movies, m_fields)}
Now if it happens that a movie does not have a corresponding imdb_data
record, i.e. Movie.query.filter_by(id=123).first().imdb_data = None
that movie's object is marshaled like this:
{
"id": 1302,
"imdb_data": {
"id": 0,
"rating": null,
"title": null
},
"title": "F 63 9 Love Sickness"
}
Instead I want the response to look like this:
{
"id": 1302,
"imdb_data": {},
"title": "F 63 9 Love Sickness"
}
I know how to hack this when I return one movie (by id):
if args['imdb_data']:
if movie.imdb_data:
m_fields['imdb_data'] = fields.Nested(imdb_data_fields)
else:
m_fields['imdb_data'] = fields.Nested({})
But how do I do that for the list of movies? Probably I could go through the array myself and change it by hand but there must be a more efficient way.
This can be achieved by creating a custom field, like this:
class NestedWithEmpty(Nested):
"""
Allows returning an empty dictionary if marshaled value is None
"""
def __init__(self, nested, allow_empty=False, **kwargs):
self.allow_empty = allow_empty
super(NestedWithEmpty, self).__init__(nested, **kwargs)
def output(self, key, obj):
value = get_value(key if self.attribute is None else self.attribute, obj)
if value is None:
if self.allow_null:
return None
elif self.allow_empty:
return {}
return marshal(value, self.nested)
and then using it to marshal objects passing allow_empty=True
:
m_fields['imdb_data'] = NestedWithEmpty(imdb_data_fields, allow_empty=True)
I even created a pull request with this feature: https://github.com/twilio/flask-restful/pull/328
After reading the PR #328 (thanks @Andriy), and following it, my fix was to add the default arg
foo['bar'] = fields.Nested(nested_fields, default={})
Wasn't obvious in the docs.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With