Relationship lazy configuration doesn't work with polymorphism

Issue #2762 resolved
Marc Schlaich created an issue

Testcase:

from sqlalchemy import create_engine, Column, ForeignKey, Integer
from sqlalchemy.orm import relationship, sessionmaker
from sqlalchemy.ext.declarative import declarative_base

Base = declarative_base()


class RelatedModel(Base):
    __tablename__ = 'related_model'
    id = Column(Integer, primary_key=True)


class Model(Base):
    __tablename__ = 'model'
    id = Column(Integer, primary_key=True)

    mtype = Column(Integer, nullable=False)
    __mapper_args__ = {'polymorphic_on': mtype}


class SubModel(Model):
    __tablename__ = 'submodel'
    id = Column(Integer, ForeignKey('model.id'), primary_key=True)
    __mapper_args__ = {'polymorphic_identity': 1}

    related_model_id = Column(Integer, ForeignKey('related_model.id'))
    related_model = relationship('RelatedModel', lazy='joined')


def main():
    engine = create_engine('sqlite:///:memory:')
    Base.metadata.create_all(engine)
    Session = sessionmaker(bind=engine)
    session = Session()

    s = SubModel()
    s.related_model = RelatedModel()
    session.add(s)

    session.commit()
    session.close()

    session = Session()
    s = session.query(Model).one()
    session.close()
    s.related_model

I expect that the relationship SubModel -> RelatedModel is already loaded, even when querying against the base class. However, the above code raises DetachedInstanceError: Parent instance <SubModel at 0x2b465f0> is not bound to a Session; lazy load operation of attribute 'related_model' cannot proceed.

Comments (2)

  1. Mike Bayer repo owner

    a polymorphic load on joined table inheritance by default only loads from the base table, not any of the related tables, until attributes specific to the subclass are accessed (as is the case with related_model here). therefore a joined eager load from SubModel->related_model is ignored here.

    The solution here is to use with_polymorphic, of which there are three ways to specify (mapper level, query.with_polymorphic(), and the newer with_polymorphic() standalone function), specifying that loads for Model should also join out to load submodels of type SubModel:

        session = Session()
        model_with_related = with_polymorphic(Model, SubModel)
        s = session.query(model_with_related).one()
        session.close()
        s.related_model
    
    # or
    
        session = Session()
        s = session.query(Model).with_polymorphic(SubModel).one()
        session.close()
        s.related_model
    
    # or
    
    Model.__mapper__.with_polymorphic = (SubModel, None)
    
  2. Marc Schlaich reporter

    If you load the polymorphic collection already as relationship via query.options(joinedload('model')), I guess only the last option works?

  3. Log in to comment