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

Infinite Loop caused by bidirectional relationships on child objects in included data #262

Open
mhxi opened this issue Oct 22, 2019 · 3 comments

Comments

@mhxi
Copy link

mhxi commented Oct 22, 2019

The issue occurs while attempting to load an object with bidirectional relationships on child objects in the included data.

Below is a minimal example demonstrating the issue. When we try to load the root data object, we run into a RecursionError. It seems marshmallow-jsonapi goes into an infinite loop trying to load the included objects when the children have bidirectional relationships.

Simple Example:

from marshmallow_jsonapi import Schema
from marshmallow_jsonapi import fields


class RootSchema(Schema):

    id = fields.Str(required=True)
    baz = fields.Str()

    first_children = fields.Relationship(
        many=True,
        type_='first_children',
        schema='FirstChildSchema'
    )

    class Meta:
        type_ = 'root'


class FirstChildSchema(Schema):

    id = fields.Str(required=True)
    foo = fields.Str()

    root = fields.Relationship(
        many=True,
        type_='root',
        schema='RootSchema'
    )

    second_children = fields.Relationship(
        many=True,
        type_='second_children',
        schema='SecondChildSchema'
    )


    class Meta:
        type_ = 'first_children'

class SecondChildSchema(Schema):

    id = fields.Str(required=True)
    bar = fields.Str()

    first_children = fields.Relationship(
        many=True,
        type_='first_children',
        schema='FirstChildSchema'
    )

    class Meta:
        type_ = 'second_children'


data = {
    'data':{
        'type': 'root',
        'id':'root_id',
        'attributes':{
            'baz': 'qwerty'
        },
        'relationships': {
            'first_children': {
                'data': [{
                    'id': 'firstchild_id',
                    'type': 'first_children'
                }]
            }
        }
    },
    'included': [{
        'id': 'firstchild_id',
        'type':'first_children',
        'attributes':{
            'foo': 'qwerty'
        },
        'relationships': {
            'second_children': {
                'data': [{
                    'id': 'secondchild_id',
                    'type': 'second_children'
                }]
            },
            'root': {
                'id': 'root_id',
                'type': 'root'
            }
        }
    }, {
        'id': 'secondchild_id',
        'type':'second_children',
        'attributes':{
            'bar': 'qwerty'
        },
        'relationships': {
            'first_children': {
                'data': [{
                    'id': 'firstchild_id',
                    'type': 'first_children'
                }]
            }
        }
    }]
}

schema = RootSchema()
print(schema.load(data))

Error:
RecursionError: maximum recursion depth exceeded

python=3.7
marshmallow=3.2.1
marshmallow-jsonapi=0.22.0

@multimeric
Copy link

What's strange is that this bug doesn't happen if you remove all references to the SecondChildSchema, even though there's also a circular relationship between Root and FirstChild. It seems that this happens only when there are relationships between multiple included data resources.

I think this is because they access related related data in slightly different ways from when it's the root element that has relationships.

@multimeric
Copy link

multimeric commented Oct 28, 2019

I've looked into solving this, but it's not easy. The best way would be to store the ret value from Unmarshaller and storing it in a dictionary indexed by id and type, and then look up that dictionary whenever we try to deserialize a field. However the Unmarshaller class doesn't provide any hooks with which we can do this. I think we'd have to modify the base marshmallow library to do this.

@retr0h
Copy link

retr0h commented Jul 12, 2020

@TMiguelT ever figure this out?

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

3 participants