--- /dev/null
+.. change::
+ :tags: bug, orm
+ :tickets: 7224
+
+ Fixed bug in "relationship to aliased class" feature introduced at
+ :ref:`relationship_aliased_class` where it was not possible to create a
+ loader strategy option targeting an attribute on the target using the
+ :func:`_orm.aliased` construct directly in a second loader option, such as
+ ``selectinload(A.aliased_bs).joinedload(aliased_b.cs)``, without explicitly
+ qualifying using :meth:`_orm.PropComparator.of_type` on the preceding
+ element of the path. Additionally, targeting the non-aliased class directly
+ would be accepted (inappropriately), but would silently fail, such as
+ ``selectinload(A.aliased_bs).joinedload(B.cs)``; this now raises an error
+ referring to the typing mismatch.
+
from sqlalchemy import and_
from sqlalchemy import Column
+from sqlalchemy import exc
from sqlalchemy import ForeignKey
from sqlalchemy import func
from sqlalchemy import Integer
from sqlalchemy.orm import Session
from sqlalchemy.testing import eq_
from sqlalchemy.testing import fixtures
+from sqlalchemy.testing.assertions import expect_raises_message
from sqlalchemy.testing.assertsql import CompiledSQL
from sqlalchemy.testing.fixtures import ComparableEntity
from sqlalchemy.testing.fixtures import fixture_session
.label("index"),
).alias()
- partitioned_b = aliased(B, alias=partition)
+ partitioned_b = cls.partitioned_b = aliased(B, alias=partition)
A.partitioned_bs = relationship(
partitioned_b,
self.assert_sql_count(testing.db, go, 2)
- def test_selectinload_w_joinedload_after(self):
+ @testing.combinations("string", "ac_attribute", "ac_attr_w_of_type")
+ def test_selectinload_w_joinedload_after(self, calling_style):
+ """test has been enhanced to also test #7224"""
+
A, B, C = self.classes("A", "B", "C")
s = Session(testing.db)
+ partitioned_b = self.partitioned_b
+
+ if calling_style == "string":
+ opt = selectinload(A.partitioned_bs).joinedload("cs")
+ elif calling_style == "ac_attribute":
+ opt = selectinload(A.partitioned_bs).joinedload(partitioned_b.cs)
+ elif calling_style == "ac_attr_w_of_type":
+ # this would have been a workaround for people who encountered
+ # #7224. The exception that was raised for "ac_attribute" actually
+ # suggested to use of_type() so we can assume this pattern is
+ # probably being used
+ opt = selectinload(
+ A.partitioned_bs.of_type(partitioned_b)
+ ).joinedload(partitioned_b.cs)
+ else:
+ assert False
+
def go():
- for a1 in s.query(A).options(
- selectinload(A.partitioned_bs).joinedload("cs")
- ):
+ for a1 in s.query(A).options(opt):
for b in a1.partitioned_bs:
eq_(len(b.cs), 2)
self.assert_sql_count(testing.db, go, 2)
+ @testing.combinations(True, False)
+ def test_selectinload_w_joinedload_after_base_target_fails(
+ self, use_of_type
+ ):
+ A, B, C = self.classes("A", "B", "C")
+
+ s = Session(testing.db)
+ partitioned_b = self.partitioned_b
+
+ if use_of_type:
+ opt = selectinload(
+ A.partitioned_bs.of_type(partitioned_b)
+ ).joinedload(B.cs)
+ else:
+ opt = selectinload(A.partitioned_bs).joinedload(B.cs)
+
+ q = s.query(A).options(opt)
+
+ with expect_raises_message(
+ exc.ArgumentError,
+ r'Attribute "B.cs" does not link from element "aliased\(B\)"',
+ ):
+ q._compile_context()
+
class AltSelectableTest(
fixtures.DeclarativeMappedTest, testing.AssertsCompiledSQL