Skip to content

Child schema's unknown setting in Meta class is ignored #216

Description

@SteggyLeggy

The unknown value for a schema's Meta class is overridden and ignored.

import marshmallow
import marshmallow.fields
from marshmallow_oneofschema import OneOfSchema


class Foo:
    def __init__(self, foo):
        self.foo = foo

    def __str__(self) -> str:
        return f"{self.foo=}"

class FooUnknown:
    def __init__(self, foo, extras):
        self.foo = foo
        self.extras = extras

    def __str__(self) -> str:
        return f"{self.foo=} - {self.extras=}"


class FooSchema(marshmallow.Schema):
    foo = marshmallow.fields.String(required=True)

    @marshmallow.post_load
    def make_foo(self, data, **kwargs):
        return Foo(**data)

class FooSchemaUnkown(marshmallow.Schema):
    class Meta:
        unknown = marshmallow.INCLUDE

    foo = marshmallow.fields.String(required=True)

    @marshmallow.post_dump(pass_original=True)
    def add_unknowns(self, output, orig, **kwargs):
        output["extras"] = orig.extras
        return output

    # def load(self, data, *, many=None, partial=None, unknown=None):
    #     """Overriding load to ensure unknown is set to INCLUDE."""
    #     return super().load(
    #         data, many=many, partial=partial, unknown=marshmallow.INCLUDE
    #     )

    @marshmallow.post_load
    def make_foo(self, data, **kwargs):
        return FooUnknown(**data)


class MyUberSchema(OneOfSchema):
    type_schemas = {"foo": FooSchema, "foo-unknown": FooSchemaUnkown}

    def get_obj_type(self, obj):
        if isinstance(obj, Foo):
            return "foo"
        elif isinstance(obj, FooUnknown):
            return "foo-unknown"
        else:
            raise Exception("Unknown object type: {}".format(obj.__class__.__name__))


dump_response = MyUberSchema().dump(Foo(foo="hello"))
assert dump_response == {"foo": "hello", "type": "foo"}

dump_response = MyUberSchema().dump(FooUnknown(foo="hello", extras={"spam": "spam"}))
assert dump_response == {"foo": "hello", "extras": {"spam": "spam"}, "type": "foo-unknown"}

load_response = MyUberSchema().load(
    {"type": "foo", "foo": "hello"}
)
assert isinstance(load_response, Foo)
assert load_response.foo == "hello"

load_response = MyUberSchema().load(
    {"type": "foo-unknown", "foo": "hello", "extras": {"spam": "spam"}}
)
assert isinstance(load_response, FooUnknown)
assert load_response.foo == "hello"
assert load_response.extras == {"spam": "spam"}

With the Meta class in the FooUnknown class I'd expect these asserts to pass. With the current way OneOfSchema works it fails when attempting to load with "extras" set with an error.

marshmallow.exceptions.ValidationError: {'extras': ['Unknown field.']}

However if I uncomment the overridden load function, which forces the load function to be passed the correct unknown field value, all asserts pass correctly.

In my own code I'm using the overriding of load workaround, but I believe the unknown field's value on the child schema should be honored. Would appreciate any advice to the contrary if I'm mistaken however.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions