Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

OneOfSchema can't use pre/post_dump/load decorators #4

Open
lafrech opened this issue Oct 19, 2016 · 5 comments · May be fixed by #130
Open

OneOfSchema can't use pre/post_dump/load decorators #4

lafrech opened this issue Oct 19, 2016 · 5 comments · May be fixed by #130

Comments

@lafrech
Copy link
Member

lafrech commented Oct 19, 2016

class MyUberSchema(OneOfSchema):
    type_schemas = {
        'foo': FooSchema,
        'bar': BarSchema,
    }

    @post_load
    def whatever(self, data):
        print("post load")

AFAIU, post_load decorated method is never called. It may be intended, or it may not be an issue, but while trying to do that it failed and I didn't see this documented so I don't know if this is known.

To get this to work, the post_load method needs to be defined in FooSchema and BarSchema (or a common parent).

@maximkulkin
Copy link
Contributor

I will look into this. Meantime, you can implement everything you need in your subschemas (like FooSchema and BarSchema)

@maximkulkin
Copy link
Contributor

I rechecked source code and I really do not want to dive into all crazy logic that original Schema class has related to pre/post processing, validation and collecting errors. I couldn't find an elegant way to plug into original Schema data dumping/loading pipeline, so I had to replace it's implementation completely thus loosing all pre/post processing.

If somebody wants to do pre/post processing regardless of particular payload type, I suggest 1) think twice; 2) implement processing in subtypes:

import marshmallow as m
import marshmallow.fields as mf

class FooSchema(m.Schema):
    foo = mf.String(required=True)

class BarSchema(m.Schema):
    bar = mf.Integer(required=True)

def my_schema_pre_load(schema, data):
    # pre process data on load
    return data

class MySchema(OneOfSchema):
    class MyFooSchema(FooSchema):
        pre_load = m.pre_load(my_schema_pre_load)

    class MyBarSchema(BarSchema):
        pre_load = m.pre_load(my_schema_pre_load)

    type_schemas = {
        'foo': MyFooSchema,
        'bar': MyBarSchema,
    }

@hjonin
Copy link

hjonin commented Nov 16, 2018

Hello,

What would you advise in case I want to do preprocessing on type field? For instance I would like to .lower() type field value (in order to be able to get the schema whatever case the type is).

Thanks in advance!

@sirosen
Copy link
Contributor

sirosen commented Nov 2, 2020

I was just looking at this because I wanted to add a post_dump method to remove the type field. I ended up overloading _dump as in

    def _dump(self, obj, **kwargs):
        data = super()._dump(obj, **kwargs)
        data.pop("type")
        return data

I bet a similar approach would work for the above case, overriding load or _load.

I looked a bit and I think at this point in time -- with the current codebase and marshmallow3-only support -- we could add these.
I probably don't have time to work on this soon, but if it sits for a while I might come back with a PR.

sirosen added a commit to sirosen/marshmallow-oneofschema that referenced this issue Nov 10, 2020
Rather than overriding the `dump()` and `load()` methods of the Schema
class, override `_serialize` and `_deserialize`, which are the
"concrete" steps in schema loading and dumping which handle loading or
dumping on a field-by-field basis.

This still uses load() and dump() methods of the type schemas being
used, but it happens between the various hooks which may run on the
OneOfSchema instance.

Add a test that checks that a `post_dump` hook to remove the `type`
field works.

The most significant downside of this change is that it makes use of
several private APIs within marshmallow. Not only are `_serialize` and
`_deserialize` private methods, but the error_store object which is
used here is also considered private (per marshmallow docs).

In order to better guarantee behavior near-identical to marshmallow,
several methods from marshmallow.utils have been copied in-tree here.

One notable API change here is that arbitrary keyword arguments are no
longer being passed from `OneOfSchema.load()` and `OneOfSchema.dump()`
down into the type schemas' load and dump methods. As a result, you
cannot specify a load or dump parameter here and expect it to take
effect.
With the switch to overriding `_serialize` and `_deserialize`, there
is no practical way to pass parameters like that.

closes marshmallow-code#4
@sirosen sirosen linked a pull request Nov 10, 2020 that will close this issue
@antoine-chopin
Copy link

I was just looking at this because I wanted to add a post_dump method to remove the type field. I ended up overloading _dump as in

    def _dump(self, obj, **kwargs):
        data = super()._dump(obj, **kwargs)
        data.pop("type")
        return data

I bet a similar approach would work for the above case, overriding load or _load.

I looked a bit and I think at this point in time -- with the current codebase and marshmallow3-only support -- we could add these. I probably don't have time to work on this soon, but if it sits for a while I might come back with a PR.

Solid workaround, thank you

@maximkulkin it would be helpful to explain in the README file that these decorators are not supported

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging a pull request may close this issue.

5 participants