stefanofontanelli / ColanderAlchemy

ColanderAlchemy helps you autogenerating Colander schemas based on SQLAlchemy mapped objects.
Other
65 stars 32 forks source link

SQLAlchemy Inheritance and backrefs #89

Open dpwrussell opened 9 years ago

dpwrussell commented 9 years ago

If I define an inherited relationship (joined-table in this case) in SQLAlchemy, the backref is not being properly understood by ColanderAlchemy.

In my contrived example there is Person, which is sub-classed in Gamer. Gamer can have some Scores associated with it.

class Person(Base):
    __tablename__ = 'person'

    satype = Column(String(50))
    __mapper_args__ = {
        'polymorphic_identity': 'person',
        'polymorphic_on': satype
    }

    id = Column(Integer, primary_key=True)
    name = Column(String(128))
    surname = Column(String(128))

class Gamer(Person):
    __tablename__ = 'gamer'

    __mapper_args__ = {
        'polymorphic_identity': 'gamer',
    }

    __colanderalchemy_config__ = {
        'excludes': ['satype']
    }

    id = Column(Integer, ForeignKey('person.id'), primary_key=True)
    high_score = Column(Integer)

class Score(Base):
    __tablename__ = 'score'

    id = Column(Integer, primary_key=True)
    gamer_id = Column(Integer, ForeignKey('gamer.id'))
    gamer = relationship('Gamer', backref='scores')
    value = Column(Integer)

This will serialize a Gamer to this:

{'high_score': <colander.null>,
 'id': '1',
 'name': u'Jayne',
 'surname': u'Cobb'}

Which is not what I was expecting. The scores backref is being ignored.

Redefine Gamer to remove use of inheritance like so:

class Gamer(Base):
    __tablename__ = 'gamer'

    id = Column(Integer, primary_key=True)
    name = Column(String(128))
    surname = Column(String(128))
    high_score = Column(Integer)

and the result is:

{'high_score': <colander.null>,
 'id': '1',
 'name': u'Jayne',
 'scores': [{'gamer_id': '1', 'id': '1', 'value': '1000'},
            {'gamer_id': '1', 'id': '2', 'value': '2000'}],
 'surname': u'Cobb'}

which is exactly what I was expecting.

Just for completeness I also tried redefining score to make use of Person instead of Gamer in its relation. This makes no difference:

class Score(Base):
    __tablename__ = 'score'

    id = Column(Integer, primary_key=True)
    gamer_id = Column(Integer, ForeignKey('person.id'))
    gamer = relationship('Person', backref='scores')
    value = Column(Integer)

Working example: https://gist.github.com/dpwrussell/f465aee4618580614f61

I think this must be a bug as I can't see why that would happen otherwise.

Cheers

tisdall commented 9 years ago

@Ademan, since you did some work on making relationship work properly, do you have any ideas about this issue?

Ademan commented 9 years ago

Sorry, semester just started, I'll take a look though.

tisdall commented 8 years ago

@Ademan - Did you have a chance to look at this?