diff options
author | mike bayer <mike_mp@zzzcomputing.com> | 2021-10-27 15:56:16 +0000 |
---|---|---|
committer | Gerrit Code Review <gerrit@ci3.zzzcomputing.com> | 2021-10-27 15:56:16 +0000 |
commit | 5a334c9a66ad83619ee6ff3dbfa5352840e76d2f (patch) | |
tree | ac1649c4c3ddc9db4f41dedf843116df63147e78 | |
parent | 0a57293b844e2164d8a71f6dcfe182bfa0732460 (diff) | |
parent | 5ad4190aa428dabc571e3d9c0e6a7944a384c8c3 (diff) | |
download | sqlalchemy-5a334c9a66ad83619ee6ff3dbfa5352840e76d2f.tar.gz |
Merge "consider "inspect(_of_type)" to be the entity of a comparator" into main
-rw-r--r-- | doc/build/changelog/unreleased_14/7244.rst | 10 | ||||
-rw-r--r-- | doc/build/orm/queryguide.rst | 2 | ||||
-rw-r--r-- | lib/sqlalchemy/orm/interfaces.py | 5 | ||||
-rw-r--r-- | lib/sqlalchemy/orm/relationships.py | 8 | ||||
-rw-r--r-- | test/orm/test_query.py | 67 |
5 files changed, 90 insertions, 2 deletions
diff --git a/doc/build/changelog/unreleased_14/7244.rst b/doc/build/changelog/unreleased_14/7244.rst new file mode 100644 index 000000000..92352c600 --- /dev/null +++ b/doc/build/changelog/unreleased_14/7244.rst @@ -0,0 +1,10 @@ +.. change:: + :tags: bug, orm, regression + :tickets: 7244 + + Fixed 1.4 regression where :meth:`_orm.Query.filter_by` would not function + correctly when :meth:`_orm.Query.join` were joined to an entity which made + use of :meth:`_orm.PropComparator.of_type` to specify an aliased version of + the target entity. The issue also applies to future style ORM queries + constructed with :func:`_sql.select`. + diff --git a/doc/build/orm/queryguide.rst b/doc/build/orm/queryguide.rst index 1703d2b5a..a10af53ba 100644 --- a/doc/build/orm/queryguide.rst +++ b/doc/build/orm/queryguide.rst @@ -602,6 +602,8 @@ appropriate constraint to use is ambiguous. the entities at the level of the mapped :class:`_schema.Table` objects are consulted when an attempt is made to infer an ON clause for the JOIN. +.. _queryguide_join_onclause: + Joins to a Target with an ON Clause ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ diff --git a/lib/sqlalchemy/orm/interfaces.py b/lib/sqlalchemy/orm/interfaces.py index 903672c80..9eb362c43 100644 --- a/lib/sqlalchemy/orm/interfaces.py +++ b/lib/sqlalchemy/orm/interfaces.py @@ -475,7 +475,8 @@ class PropComparator(operators.ColumnOperators): def of_type(self, class_): r"""Redefine this object in terms of a polymorphic subclass, - :func:`.with_polymorphic` construct, or :func:`.aliased` construct. + :func:`_orm.with_polymorphic` construct, or :func:`_orm.aliased` + construct. Returns a new PropComparator from which further criterion can be evaluated. @@ -490,6 +491,8 @@ class PropComparator(operators.ColumnOperators): .. seealso:: + :ref:`queryguide_join_onclause` - in the :ref:`queryguide_toplevel` + :ref:`inheritance_of_type` """ diff --git a/lib/sqlalchemy/orm/relationships.py b/lib/sqlalchemy/orm/relationships.py index 3916c0a83..d021ac9a2 100644 --- a/lib/sqlalchemy/orm/relationships.py +++ b/lib/sqlalchemy/orm/relationships.py @@ -1161,7 +1161,13 @@ class RelationshipProperty(StrategizedProperty): :func:`_orm.relationship`. """ - return self.property.entity + # this is a relatively recent change made for + # 1.4.27 as part of #7244. + # TODO: shouldn't _of_type be inspected up front when received? + if self._of_type is not None: + return inspect(self._of_type) + else: + return self.property.entity @util.memoized_property def mapper(self): diff --git a/test/orm/test_query.py b/test/orm/test_query.py index 1971964b1..4c3631bf4 100644 --- a/test/orm/test_query.py +++ b/test/orm/test_query.py @@ -3660,6 +3660,73 @@ class FilterTest(QueryTest, AssertsCompiledSQL): "WHERE users.id = :id_2) AS anon_1 WHERE anon_1.id = :id_3", ) + @testing.combinations((True,), (False,), argnames="use_legacy") + @testing.combinations( + ("of_type",), ("two_arg",), ("none",), argnames="join_style" + ) + def test_filter_by_against_joined_entity(self, join_style, use_legacy): + """test #7244""" + + User = self.classes.User + Address = self.classes.Address + + sess = fixture_session() + + if use_legacy: + q = sess.query(User) + else: + q = select(User) + + if join_style == "of_type": + aa = aliased(Address) + is_aliased = True + q = q.join(User.addresses.of_type(aa)) + elif join_style == "two_arg": + aa = aliased(Address) + is_aliased = True + q = q.join(aa, User.addresses) + elif join_style == "none": + aa = Address + is_aliased = False + q = q.join(User.addresses) + else: + assert False + + q = q.filter_by(email_address="fred@fred.com") + + if is_aliased: + assertsql = ( + "SELECT users.id AS users_id, users.name AS users_name " + "FROM users JOIN addresses AS addresses_1 " + "ON users.id = addresses_1.user_id " + "WHERE addresses_1.email_address = :email_address_1" + ) + else: + assertsql = ( + "SELECT users.id AS users_id, users.name AS users_name " + "FROM users JOIN addresses ON users.id = addresses.user_id " + "WHERE addresses.email_address = :email_address_1" + ) + + if use_legacy: + self.assert_compile(q, assertsql) + else: + self.assert_compile( + q.set_label_style(LABEL_STYLE_TABLENAME_PLUS_COL), assertsql + ) + + if use_legacy: + user = q.one() + else: + user = sess.execute(q).scalars().one() + + eq_( + user, + User( + name="fred", addresses=[Address(email_address="fred@fred.com")] + ), + ) + def test_filter_by_against_cast(self): """test #6414""" User = self.classes.User |