keyerror in subqueryload w complex inheritance

Issue #2617 resolved
Mike Bayer repo owner created an issue
from sqlalchemy.ext.declarative import declarative_base
from sqlalchemy import *
from sqlalchemy.orm import relationship, subqueryload, Session

Base = declarative_base()

class Z(Base):
    __tablename__ = 'z'
    id = Column(Integer, primary_key=True)

class A(Base):
    __tablename__ = 'a'
    id = Column(Integer, primary_key=True)
    type = Column(String)

    ztoa = Table('ztoa', Base.metadata,
        Column('zid', Integer, ForeignKey('z.id'), nullable=False),
        Column('aid', Integer, ForeignKey('a.id'), nullable=False)
    )
    zs = relationship("Z", secondary=ztoa, lazy="subquery", backref="as_")
    __mapper_args__ = {'polymorphic_on': type, 'with_polymorphic':'*'}

class B(A):
    __tablename__ = 'b'
    id = Column(Integer, ForeignKey('a.id'), primary_key=True)

    btod = Table('btod', Base.metadata,
        Column('bid', Integer, ForeignKey('b.id'), nullable=False),
        Column('did', Integer, ForeignKey('d.id'), nullable=False)
    )
    related = relationship("D", secondary=btod, lazy="subquery")
    __mapper_args__ = {'polymorphic_identity': 'b'}

class D(A):
    __tablename__ = 'd'
    id = Column(Integer, ForeignKey('a.id'), primary_key=True)
    __mapper_args__ = {'polymorphic_identity': 'd'}

engine = create_engine('sqlite://', echo=True)
Base.metadata.create_all(engine)
session = Session(engine)

z = Z()
d = D()
session.add_all([   Z(as_=[B(related=[d](
))])
])
session.commit()

z = session.query(Z).one()
print z.as_

Comments (6)

  1. Mike Bayer reporter

    fortunately (sort of) the #2614 branch also repairs this issue, since this is a search for "related" on the wrong parent class. Though the issue is also a regression vs. 0.7, where this somehow manages to not have this problem.

  2. Mike Bayer reporter

    great, here's why its a regression. because of #2481. here's how to reproduce in 0.7:

    diff -r f309abb638db27325f5614c797ff951d1b3fc638 lib/sqlalchemy/orm/strategies.py
    --- a/lib/sqlalchemy/orm/strategies.py  Sun Nov 18 12:36:24 2012 -0500
    +++ b/lib/sqlalchemy/orm/strategies.py  Wed Nov 28 19:29:34 2012 -0500
    @@ -730,10 +730,12 @@
                     if len(path) / 2 > self.join_depth:
                         return
                 else:
    -                if self.mapper.base_mapper in \
    -                    interfaces._reduce_path(subq_path):
    +                if self.mapper in subq_path:
    +                #if self.mapper.base_mapper in \
    +                #    interfaces._reduce_path(subq_path):
                         return
    
    +
             subq_mapper, leftmost_mapper, leftmost_attr = \
                     self._get_leftmost(subq_path)
    
  3. Mike Bayer reporter

    here's a simpler version:

    from sqlalchemy.ext.declarative import declarative_base
    from sqlalchemy import *
    from sqlalchemy.orm import relationship, subqueryload, Session
    
    Base = declarative_base()
    
    class Z(Base):
        __tablename__ = 'z'
        id = Column(Integer, primary_key=True)
    
    class A(Base):
        __tablename__ = 'a'
        id = Column(Integer, primary_key=True)
        type = Column(String)
        z_id = Column(Integer, ForeignKey('z.id'))
        zs = relationship("Z", lazy="subquery")
        __mapper_args__ = {'polymorphic_on': type, 'with_polymorphic': '*'}
    
    class B(A):
        __tablename__ = 'b'
        id = Column(Integer, ForeignKey('a.id'), primary_key=True)
    
        related = relationship("D", lazy="subquery", primaryjoin="D.b_id==B.id")
        __mapper_args__ = {'polymorphic_identity': 'b'}
    
    class D(A):
        __tablename__ = 'd'
        id = Column(Integer, ForeignKey('a.id'), primary_key=True)
        b_id = Column(Integer, ForeignKey('b.id'))
        __mapper_args__ = {'polymorphic_identity': 'd'}
    
    engine = create_engine('sqlite://', echo=True)
    Base.metadata.create_all(engine)
    session = Session(engine)
    
    session.add_all([   B()
    ](
    ))
    session.commit()
    
    a = session.query(A).first()
    
  4. Log in to comment