mirror of
https://github.com/sqlalchemy/sqlalchemy.git
synced 2026-05-25 18:12:47 -04:00
Accommodate for base class when adjusting path for with_polymorphic
Fixed an additional regression in the same area as that of 🎫`5080` introduced in 1.3.0b3 via 🎫`4468` where the ability to create a joined option across a :func:`.with_polymorphic` into a relationship against the base class of that with_polymorphic, and then further into regular mapped relationships would fail as the base class component would not add itself to the load path in a way that could be located by the loader strategy. The changes applied in 🎫`5080` have been further refined to also accommodate this scenario. Fixes: #5121 Change-Id: I9753dbbcb7b7640c995ad983a6d04b36fa18cf54
This commit is contained in:
+12
@@ -0,0 +1,12 @@
|
||||
.. change::
|
||||
:tags: orm, bug
|
||||
:tickets: 5121
|
||||
|
||||
Fixed an additional regression in the same area as that of :ticket:`5080`
|
||||
introduced in 1.3.0b3 via :ticket:`4468` where the ability to create a
|
||||
joined option across a :func:`.with_polymorphic` into a relationship
|
||||
against the base class of that with_polymorphic, and then further into
|
||||
regular mapped relationships would fail as the base class component would
|
||||
not add itself to the load path in a way that could be located by the
|
||||
loader strategy. The changes applied in :ticket:`5080` have been further
|
||||
refined to also accommodate this scenario.
|
||||
@@ -273,7 +273,6 @@ class PropRegistry(PathRegistry):
|
||||
elif (
|
||||
insp.is_aliased_class
|
||||
and insp.with_polymorphic_mappers
|
||||
and prop.parent is not insp.mapper
|
||||
and prop.parent in insp.with_polymorphic_mappers
|
||||
):
|
||||
subclass_entity = parent[-1]._entity_for_mapper(prop.parent)
|
||||
|
||||
@@ -1741,6 +1741,7 @@ class JoinedloadWPolyOfTypeContinued(
|
||||
__tablename__ = "users"
|
||||
|
||||
id = Column(Integer, primary_key=True)
|
||||
foos = relationship("Foo", back_populates="owner")
|
||||
|
||||
class Foo(Base):
|
||||
__tablename__ = "foos"
|
||||
@@ -1749,9 +1750,9 @@ class JoinedloadWPolyOfTypeContinued(
|
||||
id = Column(Integer, primary_key=True)
|
||||
type = Column(String(10), nullable=False)
|
||||
owner_id = Column(Integer, ForeignKey("users.id"))
|
||||
owner = relationship(
|
||||
"User", backref=backref("foos"), cascade="all"
|
||||
)
|
||||
owner = relationship("User", back_populates="foos")
|
||||
bar_id = Column(ForeignKey("bars.id"))
|
||||
bar = relationship("Bar")
|
||||
|
||||
class SubFoo(Foo):
|
||||
__tablename__ = "foos_sub"
|
||||
@@ -1759,8 +1760,8 @@ class JoinedloadWPolyOfTypeContinued(
|
||||
|
||||
id = Column(Integer, ForeignKey("foos.id"), primary_key=True)
|
||||
baz = Column(Integer)
|
||||
bar_id = Column(Integer, ForeignKey("bars.id"))
|
||||
bar = relationship("Bar")
|
||||
sub_bar_id = Column(Integer, ForeignKey("sub_bars.id"))
|
||||
sub_bar = relationship("SubBar")
|
||||
|
||||
class Bar(Base):
|
||||
__tablename__ = "bars"
|
||||
@@ -1769,6 +1770,13 @@ class JoinedloadWPolyOfTypeContinued(
|
||||
fred_id = Column(Integer, ForeignKey("freds.id"), nullable=False)
|
||||
fred = relationship("Fred")
|
||||
|
||||
class SubBar(Base):
|
||||
__tablename__ = "sub_bars"
|
||||
|
||||
id = Column(Integer, primary_key=True)
|
||||
fred_id = Column(Integer, ForeignKey("freds.id"), nullable=False)
|
||||
fred = relationship("Fred")
|
||||
|
||||
class Fred(Base):
|
||||
__tablename__ = "freds"
|
||||
|
||||
@@ -1776,17 +1784,66 @@ class JoinedloadWPolyOfTypeContinued(
|
||||
|
||||
@classmethod
|
||||
def insert_data(cls):
|
||||
User, Fred, Bar, SubFoo = cls.classes("User", "Fred", "Bar", "SubFoo")
|
||||
User, Fred, SubBar, Bar, SubFoo = cls.classes(
|
||||
"User", "Fred", "SubBar", "Bar", "SubFoo"
|
||||
)
|
||||
user = User(id=1)
|
||||
fred = Fred(id=1)
|
||||
bar = Bar(fred=fred)
|
||||
rectangle = SubFoo(owner=user, baz=10, bar=bar)
|
||||
sub_bar = SubBar(fred=fred)
|
||||
rectangle = SubFoo(owner=user, baz=10, bar=bar, sub_bar=sub_bar)
|
||||
|
||||
s = Session()
|
||||
s.add_all([user, fred, bar, rectangle])
|
||||
s.add_all([user, fred, bar, sub_bar, rectangle])
|
||||
s.commit()
|
||||
|
||||
def test_joined_load(self):
|
||||
def test_joined_load_lastlink_subclass(self):
|
||||
Foo, User, SubBar = self.classes("Foo", "User", "SubBar")
|
||||
|
||||
s = Session()
|
||||
|
||||
foo_polymorphic = with_polymorphic(Foo, "*", aliased=True)
|
||||
|
||||
foo_load = joinedload(User.foos.of_type(foo_polymorphic))
|
||||
query = s.query(User).options(
|
||||
foo_load.joinedload(foo_polymorphic.SubFoo.sub_bar).joinedload(
|
||||
SubBar.fred
|
||||
)
|
||||
)
|
||||
|
||||
self.assert_compile(
|
||||
query,
|
||||
"SELECT users.id AS users_id, anon_1.foos_id AS anon_1_foos_id, "
|
||||
"anon_1.foos_type AS anon_1_foos_type, anon_1.foos_owner_id "
|
||||
"AS anon_1_foos_owner_id, "
|
||||
"anon_1.foos_bar_id AS anon_1_foos_bar_id, "
|
||||
"freds_1.id AS freds_1_id, sub_bars_1.id "
|
||||
"AS sub_bars_1_id, sub_bars_1.fred_id AS sub_bars_1_fred_id, "
|
||||
"anon_1.foos_sub_id AS anon_1_foos_sub_id, "
|
||||
"anon_1.foos_sub_baz AS anon_1_foos_sub_baz, "
|
||||
"anon_1.foos_sub_sub_bar_id AS anon_1_foos_sub_sub_bar_id "
|
||||
"FROM users LEFT OUTER JOIN "
|
||||
"(SELECT foos.id AS foos_id, foos.type AS foos_type, "
|
||||
"foos.owner_id AS foos_owner_id, foos.bar_id AS foos_bar_id, "
|
||||
"foos_sub.id AS foos_sub_id, "
|
||||
"foos_sub.baz AS foos_sub_baz, "
|
||||
"foos_sub.sub_bar_id AS foos_sub_sub_bar_id "
|
||||
"FROM foos LEFT OUTER JOIN foos_sub ON foos.id = foos_sub.id) "
|
||||
"AS anon_1 ON users.id = anon_1.foos_owner_id "
|
||||
"LEFT OUTER JOIN sub_bars AS sub_bars_1 "
|
||||
"ON sub_bars_1.id = anon_1.foos_sub_sub_bar_id "
|
||||
"LEFT OUTER JOIN freds AS freds_1 "
|
||||
"ON freds_1.id = sub_bars_1.fred_id",
|
||||
)
|
||||
|
||||
def go():
|
||||
user = query.one()
|
||||
user.foos[0].sub_bar
|
||||
user.foos[0].sub_bar.fred
|
||||
|
||||
self.assert_sql_count(testing.db, go, 1)
|
||||
|
||||
def test_joined_load_lastlink_baseclass(self):
|
||||
Foo, User, Bar = self.classes("Foo", "User", "Bar")
|
||||
|
||||
s = Session()
|
||||
@@ -1795,28 +1852,31 @@ class JoinedloadWPolyOfTypeContinued(
|
||||
|
||||
foo_load = joinedload(User.foos.of_type(foo_polymorphic))
|
||||
query = s.query(User).options(
|
||||
foo_load.joinedload(foo_polymorphic.SubFoo.bar).joinedload(
|
||||
Bar.fred
|
||||
)
|
||||
foo_load.joinedload(foo_polymorphic.bar).joinedload(Bar.fred)
|
||||
)
|
||||
|
||||
self.assert_compile(
|
||||
query,
|
||||
"SELECT users.id AS users_id, anon_1.foos_id AS anon_1_foos_id, "
|
||||
"anon_1.foos_type AS anon_1_foos_type, anon_1.foos_owner_id "
|
||||
"AS anon_1_foos_owner_id, freds_1.id AS freds_1_id, bars_1.id "
|
||||
"AS bars_1_id, bars_1.fred_id AS bars_1_fred_id, "
|
||||
"anon_1.foos_sub_id AS anon_1_foos_sub_id, "
|
||||
"anon_1.foos_sub_baz AS anon_1_foos_sub_baz, "
|
||||
"anon_1.foos_sub_bar_id AS anon_1_foos_sub_bar_id "
|
||||
"FROM users LEFT OUTER JOIN "
|
||||
"(SELECT foos.id AS foos_id, foos.type AS foos_type, "
|
||||
"foos.owner_id AS foos_owner_id, foos_sub.id AS foos_sub_id, "
|
||||
"foos_sub.baz AS foos_sub_baz, foos_sub.bar_id AS foos_sub_bar_id "
|
||||
"FROM foos LEFT OUTER JOIN foos_sub ON foos.id = foos_sub.id) "
|
||||
"AS anon_1 ON users.id = anon_1.foos_owner_id "
|
||||
"LEFT OUTER JOIN bars AS bars_1 "
|
||||
"ON bars_1.id = anon_1.foos_sub_bar_id "
|
||||
"SELECT users.id AS users_id, freds_1.id AS freds_1_id, "
|
||||
"bars_1.id AS bars_1_id, "
|
||||
"bars_1.fred_id AS bars_1_fred_id, "
|
||||
"anon_1.foos_id AS anon_1_foos_id, "
|
||||
"anon_1.foos_type AS anon_1_foos_type, anon_1.foos_owner_id AS "
|
||||
"anon_1_foos_owner_id, anon_1.foos_bar_id AS anon_1_foos_bar_id, "
|
||||
"anon_1.foos_sub_id AS anon_1_foos_sub_id, anon_1.foos_sub_baz AS "
|
||||
"anon_1_foos_sub_baz, "
|
||||
"anon_1.foos_sub_sub_bar_id AS anon_1_foos_sub_sub_bar_id "
|
||||
"FROM users LEFT OUTER JOIN (SELECT foos.id AS foos_id, "
|
||||
"foos.type AS foos_type, "
|
||||
"foos.owner_id AS foos_owner_id, foos.bar_id AS foos_bar_id, "
|
||||
"foos_sub.id AS "
|
||||
"foos_sub_id, foos_sub.baz AS foos_sub_baz, "
|
||||
"foos_sub.sub_bar_id AS "
|
||||
"foos_sub_sub_bar_id FROM foos "
|
||||
"LEFT OUTER JOIN foos_sub ON foos.id = "
|
||||
"foos_sub.id) AS anon_1 ON users.id = anon_1.foos_owner_id "
|
||||
"LEFT OUTER JOIN bars "
|
||||
"AS bars_1 ON bars_1.id = anon_1.foos_bar_id "
|
||||
"LEFT OUTER JOIN freds AS freds_1 ON freds_1.id = bars_1.fred_id",
|
||||
)
|
||||
|
||||
|
||||
+40
-1
@@ -1011,6 +1011,45 @@ class PathRegistryInhTest(_poly_fixtures._Polymorphic):
|
||||
),
|
||||
)
|
||||
|
||||
def test_with_poly_base_two(self):
|
||||
Company = _poly_fixtures.Company
|
||||
Person = _poly_fixtures.Person
|
||||
Engineer = _poly_fixtures.Engineer
|
||||
cmapper = inspect(Company)
|
||||
pmapper = inspect(Person)
|
||||
|
||||
p_poly = with_polymorphic(Person, [Engineer])
|
||||
e_poly_insp = inspect(p_poly.Engineer) # noqa - used by comment below
|
||||
p_poly_insp = inspect(p_poly)
|
||||
|
||||
p1 = PathRegistry.coerce(
|
||||
(
|
||||
cmapper,
|
||||
cmapper.attrs.employees,
|
||||
p_poly_insp,
|
||||
pmapper.attrs.paperwork,
|
||||
)
|
||||
)
|
||||
|
||||
eq_(
|
||||
p1.path,
|
||||
(
|
||||
cmapper,
|
||||
cmapper.attrs.employees,
|
||||
p_poly_insp,
|
||||
pmapper.attrs.paperwork,
|
||||
),
|
||||
)
|
||||
eq_(
|
||||
p1.natural_path,
|
||||
(
|
||||
cmapper,
|
||||
cmapper.attrs.employees,
|
||||
pmapper,
|
||||
pmapper.attrs.paperwork,
|
||||
),
|
||||
)
|
||||
|
||||
def test_nonpoly_oftype_aliased_subclass_onroot(self):
|
||||
Engineer = _poly_fixtures.Engineer
|
||||
eng_alias = aliased(Engineer)
|
||||
@@ -1094,7 +1133,7 @@ class PathRegistryInhTest(_poly_fixtures._Polymorphic):
|
||||
),
|
||||
)
|
||||
|
||||
def test_with_poly_base(self):
|
||||
def test_with_poly_base_one(self):
|
||||
Person = _poly_fixtures.Person
|
||||
Engineer = _poly_fixtures.Engineer
|
||||
pmapper = inspect(Person)
|
||||
|
||||
Reference in New Issue
Block a user