Search code examples
pythonmarshmallow

How to make marshmallow raise AttributeErrors on serialization


How can I make marshmallow make raise an exception when data is missing attributes?

Consider the following example:

In [8]: import marshmallow

In [9]: class Foo(marshmallow.Schema):
   ...:     bar = marshmallow.fields.Str(required=True)
   ...:

In [10]: class Bar:
    ...:     pass
    ...:

In [11]: bar = Bar()

In [12]: Foo().dumps(bar)
Out[12]: MarshalResult(data='{}', errors={})

In [13]: bar.bar
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
<ipython-input-13-6901e83d9f0c> in <module>()
----> 1 bar.bar

AttributeError: 'Bar' object has no attribute 'bar'

What's even worse, is this:

In [14]: foo.loads(foo.dumps(bar).data)
Out[14]: UnmarshalResult(data={}, errors={'bar': ['Missing data for required field.']})

Solution

  • The following quote from this issue explains the behavior you're describing in the first part of your question.

    This is by design. Primarily for performance reasons, validation only happens on deserialization (load and validate). The data passed to dump is assumed to be valid.

    The expectation is that you'll explicitly call validate(), if you need validation, e.g. from your example:

    In [10]: foo.validate(foo.dump(foo).data)
    ---------------------------------------------------------------------------
    ValidationError                           Traceback (most recent call last)
    <ipython-input-40-4dfb5988e928> in <module>()
    ----> 1 foo.validate(foo.dump(foo).data)
    
    ...
    
    ValidationError: {'bar': ['Missing data for required field.']}
    

    Regarding the lack of validation during deserialization, for the version of marshmallow that you're using you have to explicitly declare the schema to be 'strict' in order for validation to happen automatically. In your example, this can be accomplished with:

    In [22]: class Foo(marshmallow.Schema):
        ...:     bar = marshmallow.fields.Str(required=True)
        ...:     class Meta:
        ...:         strict = True
    
    In [23]: foo = Foo()
    
    In [24]: foo.loads(foo.dumps(bar).data)
    ---------------------------------------------------------------------------
    ...
    ValidationError: {'bar': ['Missing data for required field.']}
    

    As discussed in Issue 598, this behavior was changed and the latest version of marshamallow defaults to this 'strict' behavior; it also removes the MarshalResult/UnmarshalResult wrappers.