Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Automatic dictionary key resolution with nested schemas using Marshmallow

I have a Marshmallow schema where an objects use a key to refer to an object that is defined in a dictionary in another part of the structure. I want to have the key automatically resolved when deserializing the object. How can I achieve this effect in Marshmallow in an idiomatic manner?

The workaround for now is to resolve all of the references manually, but this seems clunky, since the declarative nature of Marshmallow should be able to do it for us automatically.

Note that Marshmallow-SQLAlchemy supports this kind of (de-)serialization when columns are declared as relationships" which do this automatically for us, but I want to do it with JSON data.

Here is an example of what I want to achieve, with fields.Relationship being something that does not exist yet:

class UserSchema(Schema):
    name = fields.String()
    email = fields.Email()
    friends = fields.Relationship('self', path="AddressBook.contacts", many=True)

class AddressBookSchema(Schema):
    contacts = nested.Dict(keys=fields.String(), values=fields.Nested(UserSchema))

# ... create ``user`` ...
serialized_data = AddressBookSchema().dump(user)
pprint(serialized_data)
# "contacts": {
#   "Steve": {
#     "name": "Steve",
#     "email": "[email protected]",
#     "friends": ["Mike"]
#   },
#   "Mike": {
#     "name": "Mike",
#     "email": "[email protected]",
#     "friends": []
# }


deserialized_data = UserSchema().load(result)
pprint(deserialized_data)
# "contacts": {
#   "Steve": {
#     "name": "Steve",
#     "email": "[email protected]",
#     "friends": [ {"name": "Mike", "email": "[email protected]"]
#   },
#   "Mike": {
#     "name": "Mike",
#     "email": "[email protected]",
#     "friends": []
# }

I have also filed an issue on the Marshmallow Github repository.

like image 805
cdiggins Avatar asked Aug 23 '18 18:08

cdiggins


1 Answers

why not simply transfer the intermediate data with a post_load hook:

class UserSchema(Schema):
    name = fields.String()
    email = fields.Email()
    friends = fields.List(fields.String())

class AddressBookSchema(Schema):
    contacts = fields.Dict(keys=fields.String(), values=fields.Nested(UserSchema))

    @post_load
    def trans_friends(self, item):
        for name in item['contacts']:
            item['contacts'][name]['friends'] = [item['contacts'][n] for n in item['contacts'][name]['friends']]


data = """
{
 "contacts": {
  "Steve": {
    "name": "Steve",
    "email": "[email protected]",
    "friends": ["Mike"]
  },
  "Mike": {
    "name": "Mike",
    "email": "[email protected]",
    "friends": []
  }
 }
}
"""

deserialized_data = AddressBookSchema().loads(data)
pprint(deserialized_data)

yields:

UnmarshalResult(data={'contacts': {'Steve': {'name': 'Steve', 'email': '[email protected]', 'friends': [{'name': 'Mike', 'email': '[email protected]', 'friends': []}]}, 'Mike': {'name': 'Mike', 'email': '[email protected]', 'friends': []}}}, errors={})
like image 88
georgexsh Avatar answered Nov 16 '22 23:11

georgexsh