Hey,
From the dictionary:
data = {
'id': 1337,
'profiles': [
{'email': '[email protected]'},
{'email': '[email protected]'},
]
}
Which schema could I write to dump the following result?
{
"id": 1337,
"current_email": "[email protected]"
}
Assuming the latest email in the first entry from profiles.
I imagine it should be possible with fields.Pluck, but I can't make it work.
Thanks!
I found a hack.
Schema.dump() calls get_attribute, which calls marshmallow.utils.get_value to handle cases when the field attribute is a dot-separated string. For example with fields.String(attribute='obj.value') the serialized value is retrieved from the field value of obj.
Unfortunately utils.get_value doesn't accept array accessors, so attribute=profiles.0.email is not valid.
It is, however, possible to override get_attribute such as:
from marshmallow import fields, Schema
data = {
'id': 1337,
'profiles': [
{'email': '[email protected]'},
{'email': '[email protected]'},
]
}
class ProfileSchema(Schema):
email = fields.String()
class UserSchema(Schema):
id = fields.Int()
email = fields.String(attribute=lambda obj: obj['profiles'][0]['email'])
def get_attribute(self, obj, attr, default):
if callable(attr):
return attr(obj)
return super().get_attribute(obj, attr, default)
print (UserSchema().dump(data))
This example does not check for errors and assumes profiles is always a list of at least one element with an email field.
Also, this method breaks schema loading, and there is no obvious way to fix it (but I don't need it anyway).
You could also use a custom field and implement _serialize.
Sidenote: Are you sure you want to be dump-ing a dictionary? Typically you load dictionaries into your application objects.
Closing for now, but feel free to continue discussion
How can I create this custom field? With the following data and schema:
data = {
'id': 1337,
'profiles': [
{'email': '[email protected]'},
{'email': '[email protected]'},
]
}
class MyField(fields.Field):
def _serialize(self, value, attr, obj, **kwargs):
# never called
print(self, value, attr, obj, kwargs)
class UserSchema(Schema):
id = fields.Int()
email = MyField()
print (UserSchema().dump(data))
MyField._serialize is never called since email doesn't exist in the payload.
Oh right, custom field isn't what you want. You could use a post_dump method instead.
Indeed, it is also working. Full working example:
from marshmallow import decorators, fields, Schema
data = {
'id': 1337,
'profiles': [
{'email': '[email protected]'},
{'email': '[email protected]'},
]
}
class ProfileSchema(Schema):
email = fields.String()
class UserSchema(Schema):
id = fields.Int()
@decorators.post_dump(pass_original=True)
def add_email(self, data, original_data, many=False):
data['email'] = original_data['profiles'][0]['email']
return data
print (UserSchema().dump(data))
pass_original is important here. By default, original data is not given to post_dump.
Thanks!
Most helpful comment
Indeed, it is also working. Full working example:
pass_originalis important here. By default, original data is not given topost_dump.Thanks!