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

Self-referential nested fields not parsed correctly #55

Closed
whoiswes opened this issue Feb 25, 2016 · 5 comments

Comments

@whoiswes
Copy link

commented Feb 25, 2016

Apologies if I'm missing something obvious here as I'm still figuring out Marshmallow and APISpec.

When we have a Model with a self-referential relationship, apispec is not able to parse the child relationships successfully. Here's an example (SQLAlchemy, Marshmallow-SQLAlchemy, APISpec, and Flask)

Models:

Parent_Child = Table('Parent_Child', Base.metadata,
    Column('parent_id', Integer, ForeignKey('Entity.id')),
    Column('child_id', Integer, ForeignKey('Entity.id'))
    )

class Entity(Base):
    __tablename__ = 'Entity'
    id = Column(Integer, primary_key=True)
    name = Column(String(80))
    children = relationship('Entity', secondary='Parent_Child', primaryjoin=(id == Parent_Child.c.parent_id), secondaryjoin=(id == Parent_Child.c.child_id), lazy='joined', backref='parent')

Schema:

class EntitySchema(ModelSchema):
    children = fields.Nested('self', many=True)
    class Meta:
        model = Entity
        sqla_session = session
        include_fk = True

EntitySerializer = EntitySchema()

APISpec to_dict output (note the Entity.children object reference)

{
    'parameters': {},
    'paths': {
        '/entities': {}
    },
    'swagger': '2.0',
    'info': {
        'version': '1.0.0',
        'title': 'M2M Nested Example'
    },
    'description': 'M2M Nested Example',
    'definitions': {
        'Entity': {
            'properties': {
                'children': .at 0x00000000048BF7B8 > ,
                'id': {
                    'format': 'int32',
                    'type': 'integer'
                },
                'parent': {
                    'type': 'array',
                    'items': {
                        'type': 'string'
                    }
                },
                'name': {
                    'type': 'string'
                }
            }
        }
    }
}

Again, apologies if I've missed something obvious or this is a known issue. I can provide a complete sample app via gist if that would be helpful.

Thanks!

@whoiswes

This comment has been minimized.

Copy link
Author

commented Mar 30, 2016

FYI, by accident I got this working. If you change 'self' to the name of the schema you're self-referencing, APISpec doesn't blow up.

Like so:

class EntitySchema(ModelSchema):
    children = fields.Nested('EntitySchema', many=True)
    class Meta:
        model = Entity
        sqla_session = session
        include_fk = True

Not sure if this would be expected or desired behavior so leaving this open for now.

@sloria

This comment has been minimized.

Copy link
Member

commented Apr 8, 2016

Thanks for the update, @whoiswes . I'm glad you found a workaround, but this still looks to be a bug--passing 'self' should not break the code.

I would be open to a PR for this.

@sloria

This comment has been minimized.

Copy link
Member

commented Apr 10, 2016

This bug is now fixed on dev.

@sloria sloria removed the help wanted label Apr 10, 2016

@Chris2048

This comment has been minimized.

Copy link

commented Jun 19, 2018

Hi, Can you explain to me the part of this fix:

is_unbound_self_referencing = not getattr(field, 'parent', None) and field.nested == 'self'

How might a self-referencing Nested field be 'bounded'?
Is the test for a parent is the same as a test for being an instance of SchemaABC (top-level abs baseclass)?

I am getting a MaxRecursion error on a self-referencing Nested field. This is fixed by adding a ref, but I'm curious why it is not USR without - the test for there being no parent fails, so the code here doesn't seem to handle.

Regards.

@Chris2048

This comment has been minimized.

Copy link

commented Jun 21, 2018

oooh, I think I see now - it if you supply a definition via:

spec.definition('foo', schema=FooSchema)

It will then be a "top-level" object since it has its own definition.
Then the name ('foo') is used to fill in the ref.

Is it possible to raise an exception when the object is self-referential but not top-level?
i.e."Self-referencing schema must provide named definition".

Incidentally, what happens when there are Schema that don't involve self-reference, but are recursive, e.g. involving two elements that refer to each other, A -> B -> A -> B -> A -> ...

If neither provides a definition then the same MaxRecursion will occur, without a Nested 'self'?

Regards.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
3 participants
You can’t perform that action at this time.