Search code examples
marshmallow

Inheriting "exclude" meta parameter from super schema (marshmallow)


I have an hierarchy of objects and an hierarchy of schemas corresponding to them. A schema at an intermediate level of this hierarchy excludes a specific inherited field. I expect that schemas inheriting from it will "inherit" this exclusion, but this does not seem to be the case if they add their own excluded fields in their Meta classes:

from marshmallow import fields
from marshmallow.schema import Schema


class AncestorSchema(Schema):
    a = fields.Str()
    b = fields.Str()


class IntermediateSchema(AncestorSchema):
    c = fields.Str()

    class Meta:
        exclude = ('b',)


class FinalSchema(IntermediateSchema):
    d = fields.Str()

    class Meta:
        exclude = ('c',)


value = dict(
    a="Field A",
    b="Field B",
    c="Field C",
    d="Field D"
)

print(IntermediateSchema().dump(value).data)

>>> {'c': 'Field C', 'a': 'Field A'}

print(FinalSchema().dump(value).data)

>>> {'d': 'Field D', 'a': 'Field A', 'b': 'Field B'}

In the example above, FinalSchema inherits from IntermediateSchema (which excludes field b) and excludes field c in its own Meta class. An expected behavior would be that the resulting schema will exclude both b and c, but actually it excludes only c.

Of course it's possible to manually include superschema's excluded fields the inheriting schema's excluded fields, but that's not the point of inheritance, and besides, it's cumbersome.

I wonder whether the desired behavior can be achieved in an elegant way, or whether the current behavior of schema inheritance is in fact a bug.

Inspecting the source code of marshmallow shows that inheritance of data from superschemas' meta classes is at least partially supported (namely, ordered Meta option value is inherited from superschemas).


Solution

  • The other answer doesn't work, because self is not defined. I've found a solution that does work.

    from marshmallow import fields
    from marshmallow.schema import Schema
    
    
    class AncestorSchema(Schema):
        a = fields.Str()
        b = fields.Str()
    
    
    class IntermediateSchema(AncestorSchema):
        c = fields.Str()
    
        class Meta:
            exclude = ('b',)
    
    
    class FinalSchema(IntermediateSchema):
        d = fields.Str()
    
        def __init__(self, *args, **kwargs):
            self.opts.exclude += ('c',)
            super().__init__(*args, **kwargs)