Marshmallow: Allow load to add missing fields with default values

Created on 17 Apr 2018  ·  5Comments  ·  Source: marshmallow-code/marshmallow

When I have something like this:

from marshmallow import Schema, fields

class UserSchema(Schema):
    name = fields.Str(required=True)
    email = fields.Email(required=True)
    created_at = fields.DateTime()
    some_other = fields.Str(default='This is something else')

user_data = {
    'created_at': '2014-08-11T05:26:03.869245',
    'email': u'[email protected]',
    'name': u'Ken'
}
schema = UserSchema()
result = schema.load(user_data)
pprint(result)

I currently get:

{'created_at': datetime.datetime(2014, 8, 11, 5, 26, 3, 869245),
 'email': '[email protected]',
 'name': 'Ken'}

I would expect that some_other would be added with the default set to This is something else, especially if we are passing this to @post_load to create an object so that I don't have to have said object also handle setting the default (and potentially getting out of sync).

This is especially handy when using marshmallow to validate and transform the data as necessary on a load before passing it further down the stack to avoid having to add a bunch of if key in obj statements.

Currently I end up doing:

result = schema.dump(schema.load(user_data))
pprint(result)

Which does have the desired result:

{'created_at': '2014-08-11T05:26:03.869245+00:00',
 'email': '[email protected]',
 'name': 'Ken',
 'some_other': 'This is something else'}

but is doing a lot of extra work that shouldn't be necessary.


Is there already a recipe for doing something like this with a @post_load decorator? If so I don't mind using that.

Most helpful comment

It does seem arbitrary for default to only be used for serialization. It isn't immediately obvious that missing is the deserialization complement. It would be nice if there was a little more symmetry here. Maybe something like default_load, default_dump, and default to set both at once.

All 5 comments

If only I had the nagging feeling to check the docs for Field before I opened this issue and found missing.

Sorry for the noise!

It does seem arbitrary for default to only be used for serialization. It isn't immediately obvious that missing is the deserialization complement. It would be nice if there was a little more symmetry here. Maybe something like default_load, default_dump, and default to set both at once.

@deckar01 this sounds reasonable. Would you like to open a RFC to discuss this for marshmallow 3?

I've used both missing and default but it doesn't add the field. What am I missing?

Using:
marshmallow==3.0.0b8
marshmallow-enum==1.4.1
marshmallow-sqlalchemy==0.13.2

I've used both missing and default but it doesn't add the field. What am I missing?

Using:
marshmallow==3.0.0b8
marshmallow-enum==1.4.1
marshmallow-sqlalchemy==0.13.2

Do something like this;

from marshmallow import Schema, fields, pre_load
from random import randint
from uuid import uuid4

class Testing(Schema):
    user_id = fields.Integer(missing=randint(1, 100), allow_none=True)
    name = fields.String(missing=str(uuid4()), allow_none=True)

    @pre_load
    def delete_none_values(self, in_data, **kwargs):
        """Delete all None values so they get filled out with their 'missing' parameters."""
        to_delete = []
        for key, value in in_data.items():
            if value is None:
                # can't delete dict values on the fly, it produces an error
                to_delete.append(key)
        for key in to_delete:
            del in_data[key]
        return in_data

Testing().load(dict(name=None))
Was this page helpful?
0 / 5 - 0 ratings