How to use Schema.from_dict() for nested dictionaries?

Viewed 976

I am trying to create a Schema class using nested dictionaries that has some list as elements. However when I do a dumps() Only the top level elements are dumped.

Have a rest api that returns a list of certain things,eg. list of users. but the schema is such that certain aggregate details are sent at the top level, the data looks something like this. This is what i am expecting as output:

   {
        "field1": 5,
        "field2": false,
        "field3": {
            "field4": 40,
            "field5": [
                        {
                            "field6": "goo goo gah gah",
                            "field7": 99.341879,
                            "field8": {
                                "field9": "goo goo gah gah",
                                "field10": "goo goo gah gah"
                        }
             }]
         }
    }  

Heres my code:

MySchema = Schema.from_dict(
   {
        "field1": fields.Int(),
        "field2": fields.Bool(),
        "field3": {
            "field4": fields.Int(),
            "field5": [
                        {
                            "field6": fields.Str(),
                            "field7": fields.Float(),
                            "field8": {
                                "field9": fields.Str(),
                                "field10": fields.Str()
                        }
             }]
         }
    }            
)

#Then use it like:

response = MySchema().dumps(data)

Actual result:

"{\"field1\": 5, \"field2\": false}"
1 Answers

Option 1

You're looking for several nested schemas, interconnected through fields.Nested:

from marshmallow import Schema, fields


Field8Schema = Schema.from_dict({
    "field9": fields.Str(), 
    "field10": fields.Str()
})

Field5Schema = Schema.from_dict({
    "field6": fields.Str(),
    "field7": fields.Float(),
    "field8": fields.Nested(Field8Schema),
})

Field3Schema = Schema.from_dict({
    "field4": fields.Int(), 
    "field5": fields.List(fields.Nested(Field5Schema))
})

MySchema = Schema.from_dict({
    "field1": fields.Int(),
    "field2": fields.Bool(),
    "field3": fields.Nested(Field3Schema),
})

MySchema().dump(data)

# {'field2': False,
#  'field1': 5,
#  'field3': {'field4': 40,
#   'field5': [{'field6': 'goo goo gah gah',
#     'field8': {'field9': 'goo goo gah gah', 'field10': 'goo goo gah gah'},
#     'field7': 99.341879}]}}

Option 2

If the nesting won't be that deep, it might be simpler to use decorators, i.e. nest and unnest data as suggested in the docs:

class UserSchema(Schema):
    @pre_load(pass_many=True)
    def remove_envelope(self, data, many, **kwargs):
        namespace = 'results' if many else 'result'
        return data[namespace]

    @post_dump(pass_many=True)
    def add_envelope(self, data, many, **kwargs):
        namespace = 'results' if many else 'result'
        return {namespace: data}

It feels it fits your case nicely.

Comments

I'd suggest not to use from_dict as it is less readable for such a complex data, and instead switch to a class-based schema.

There's plenty of good examples of nesting in the docs.

Related