Reputation: 76
How to group fields together when serialising a flat-structured SQLAlchemy object with Marshmallow without changing the flat data structure in the background?
Suppose a SQLAlchemy model in a Flask app like this:
from app import db # db using SQLAlchemy
class Data(db.Model):
x = db.Column(db.Float(), required=True)
y = db.Column(db.Float(), required=True)
d = db.Column(db.Float())
How can I serialise this object so that x
and y
are nested into coordinates
, while maintaining a flat data structure in the background (the model)? The output should look something like this:
{
"coordinates": {
"x": 10.56
"y": 1
},
"d": 42.0
}
The problem arises specifically because I use the Data
schema with the many=True
option. The initialisation is roughly:
schema_data = MomentData()
schema_datas = MomentData(many=True)
So this is what I've tried so far, but none of them seemed to work.
Adding a second schema and modifying the Data
schema from before yields:
class CoordinatesSchema(Schema):
x = fields.Float(required=True)
y = fields.Float(required=True)
class DataSchema(Schema):
coordinates = fields.Nested(coordinatesSchema, required=True)
d = fields.Float()
Having that in place raises the problem of having to go through every Data
item and manually adding the Coordinates
schema. My data is coming from a SQLAlchemy query returning a list of Data
objects, so that I can easily dump them using schema_datas
.
fields.Dict
Since Marshmallows fields
module offers a dictionary, I tried that as well.
def DataSchema(Schema):
coordinates = fields.Dict(keys=fields.String(),
value=fields.Float(),
required=True,
default={
"x": Data.x,
"y": Data.y
})
d = fields.Float()
Doesn't seem to work either, because Marshmallow can't find Data.x
and Data.y
automatically when using schema_datas.dump()
.
The most logical solution path would be to self-nest. But (from what I understood reading the documentation) self-nesting only refers to nesting one or more other instances within the object. I want to nest the same instance.
class DataSchema(Schema):
x = fields.Float(required=True, load_only=True)
y = fields.Float(required=True, load_only=True)
coordinates = fields.Nested(
lambda: DataSchema(only=('x', 'y')),
dump_only=True)
But unfortunately this also didn't work.
@pre_dump
Inspired by this issue on Marshmallow's Github page, I tried to use the @pre_dump
decorator to achieve the desired outcome, but failed again.
class CoordinatesSchema(Schema):
x = fields.Float(required=True)
y = fields.Float(required=True)
class DataSchema(Schema):
coordinates = fields.Nested(coordinatesSchema, required=True)
d = fields.Float()
@pre_dump
def group_coordinates(self, data, many):
return {
"coordinates": {
"x": data.x,
"y": data.y
},
"d": data.d
}
But I can't figure out how to do it properly...
So my question is, what am I doing wrong and how can I solve this problem?
Upvotes: 2
Views: 815
Reputation: 1
After struggling myself with same issue, here is the solution (not so far from what you suggested) :
class CoordinatesSchema(Schema):
x = fields.Float(required=True)
y = fields.Float(required=True)
class DataSchema(Schema):
coordinates = fields.Nested(coordinatesSchema, required=True)
d = fields.Float()
@pre_dump
def group_coordinates(self, data, many):
data.coordinates = data
return(data)
Upvotes: 0