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

Different order of pre_load hooks for List(Nested) and Nested(many=True) fields #1942

Open
shashkin opened this issue Feb 2, 2022 · 1 comment

Comments

@shashkin
Copy link

shashkin commented Feb 2, 2022

Hello, I found a strange difference in load behavior between List(Nested) and Nested(many=True)
Consider the following example:

from copy import deepcopy
from marshmallow import Schema, fields, pre_load

class InnerSchema(Schema):
    value = fields.String()

    @pre_load(pass_many=False)
    def add_prefix(self, data, **_):
        print('pre_load inner')
        data = deepcopy(data)
        data['value'] = '_'.join([self.context.get('prefix'), data['value']])
        return data

class MiddleSchema(Schema):
    prefix = fields.String()
    inner = fields.Nested(InnerSchema)

    @pre_load(pass_many=False)
    def store_prefix(self, data, **_):
        print('pre_load middle')
        self.context['prefix'] = data['prefix']
        return data

class ListNestedSchema(Schema):
    data = fields.List(fields.Nested(MiddleSchema))

class NestedManySchema(Schema):
    data = fields.Nested(MiddleSchema, many=True)

obj = {'data': [
    {'prefix': 'foo', 'inner': {'value': 'x'}},
    {'prefix': 'bar', 'inner': {'value': 'z'}}
]}

In this synthetic example I have a list of objects, and I need each of them to pass its own piece if data to nested ones on loading.
I'm using context to pass this piece of data to nested schema and expecting each of InnerSchema to receive its own piece of data corresponding to exact parent object.
Loading data with ListNestedSchema does exactly what I expect, however NestedManySchema works in a different manner. It looks like the reason is that the order of pre_load hooks execution differs and I'm not sure if it was designed in such way or is it a bug.

>>> ListNestedSchema().load(obj)
pre_load middle
pre_load inner
pre_load middle
pre_load inner
{'data': [{'inner': {'value': 'foo_x'}, 'prefix': 'foo'}, {'inner': {'value': 'bar_z'}, 'prefix': 'bar'}]}


>>> NestedManySchema().load(obj)
pre_load middle
pre_load middle
pre_load inner
pre_load inner
{'data': [{'inner': {'value': 'bar_x'}, 'prefix': 'foo'}, {'inner': {'value': 'bar_z'}, 'prefix': 'bar'}]}

I'm using marshmallow==3.14.1 with Python 3.8.2

@shashkin
Copy link
Author

shashkin commented Feb 2, 2022

I guess this difference is worth to notice in #779
In my project I had to use exactly List(Nested(...)) field, because I use the same context key for storing each prefix, but I guess if I had an option to access the parent object, like in #940 , I could store all prefixes in context separately and get the exact one in InnerSchema based on which parent object is passed.
In that case I could be using either of List(Nested(...)) or Nested(..., many=True) fields

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

No branches or pull requests

1 participant