Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

Tags:

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).

like image 431
Inego Avatar asked Dec 06 '17 08:12

Inego


2 Answers

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)
like image 196
flayman Avatar answered Sep 30 '22 08:09

flayman


You also need to specify the base class for the Meta class. You also need to use some sort of reflection to get the value from the base class and append to it.

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)
like image 34
CaptObvious Avatar answered Oct 03 '22 08:10

CaptObvious