allow of_type() strategies to be stacked

Issue #3256 resolved
Mike Bayer repo owner created an issue
from sqlalchemy import Integer, ForeignKey, String, Column, create_engine
from sqlalchemy.orm import relationship, joinedload, with_polymorphic, Session
from sqlalchemy.ext.declarative import declarative_base

Base = declarative_base()

import logging
logging.basicConfig()
logging.getLogger("sqlalchemy.orm.path_registry").setLevel(logging.DEBUG)


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


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

    __mapper_args__ = {
        'polymorphic_on': type,
    }


class BSub1(B):
    __mapper_args__ = {
        'polymorphic_identity': 'sub1',
    }
    c = relationship("C1", uselist=False)


class BSub2(B):
    __mapper_args__ = {
        'polymorphic_identity': 'sub2',
    }
    c = relationship("C2", uselist=False)


class C1(Base):
    __tablename__ = 'c1'
    id = Column(Integer, primary_key=True)
    b_id = Column(ForeignKey('b.id'))


class C2(Base):
    __tablename__ = 'c2'
    id = Column(Integer, primary_key=True)
    b_id = Column(ForeignKey('b.id'))

e = create_engine("sqlite://", echo='debug')
Base.metadata.create_all(e)

s = Session(e)
s.add_all([
    A(b=BSub1(c=C1())),
    A(b=BSub2(c=C2()))
])
s.commit()

b_poly = with_polymorphic(B, [BSub1, BSub2], aliased=True)

q = s.query(A).options(
    # works
    # joinedload(A.b.of_type(b_poly)).joinedload(b_poly.BSub1.c),
    # joinedload(A.b.of_type(b_poly)).joinedload(b_poly.BSub2.c),
    # fails, eager loads only one of them
    joinedload(A.b.of_type(BSub1)).joinedload(BSub1.c),
    joinedload(A.b.of_type(BSub2)).joinedload(BSub2.c),
)

for a in q:
    b = a.b
    print b.c

Comments (1)

  1. Mike Bayer reporter
    • The :meth:.PropComparator.of_type modifier has been improved in conjunction with loader directives such as :func:.joinedload and :func:.contains_eager such that if two :meth:.PropComparator.of_type modifiers of the same base type/path are encountered, they will be joined together into a single "polymorphic" entity, rather than replacing the entity of type A with the one of type B. E.g. a joinedload of A.b.of_type(BSub1)->BSub1.c combined with joinedload of A.b.of_type(BSub2)->BSub2.c will create a single joinedload of A.b.of_type((BSub1, BSub2)) -> BSub1.c, BSub2.c, without the need for the with_polymorphic to be explicit in the query. fixes #3256

    → <<cset de11f9498258>>

  2. Log in to comment