mirror of
https://github.com/sqlalchemy/sqlalchemy.git
synced 2026-05-17 14:17:29 -04:00
Ensure propagate_attrs available on PropComparator
Fixed regression caused by just-released performance fix mentioned in #6550 where a query.join() to a relationship could produce an AttributeError if the query were made against non-ORM structures only, a fairly unusual calling pattern. In this fix, since we are no longer going through the production of ``__clause_element__()`` for Cls.some_relationship, I assumed we just throw this object away completely but I missed the one little bit where we might be getting ``_propagate_attrs`` from it. So we implement ``_propagate_attrs`` on ``PropComparator`` as well, since this is easy to define. Fixes: #6558 Change-Id: If781bf844e7e3d3b0841aff1c3668e9d6af9f097
This commit is contained in:
+8
@@ -0,0 +1,8 @@
|
||||
.. change::
|
||||
:tags: bug, orm, regression
|
||||
:tickets: 6558
|
||||
|
||||
Fixed regression caused by just-released performance fix mentioned in #6550
|
||||
where a query.join() to a relationship could produce an AttributeError if
|
||||
the query were made against non-ORM structures only, a fairly unusual
|
||||
calling pattern.
|
||||
@@ -425,6 +425,18 @@ class PropComparator(operators.ColumnOperators):
|
||||
compatible with QueryableAttribute."""
|
||||
return inspect(self._parententity).mapper
|
||||
|
||||
@property
|
||||
def _propagate_attrs(self):
|
||||
# this suits the case in coercions where we don't actually
|
||||
# call ``__clause_element__()`` but still need to get
|
||||
# resolved._propagate_attrs. See #6558.
|
||||
return util.immutabledict(
|
||||
{
|
||||
"compile_state_plugin": "orm",
|
||||
"plugin_subject": self._parentmapper,
|
||||
}
|
||||
)
|
||||
|
||||
@property
|
||||
def adapter(self):
|
||||
"""Produce a callable that adapts column expressions
|
||||
|
||||
@@ -884,6 +884,9 @@ class JoinTargetImpl(RoleImpl):
|
||||
self, original_element, resolved, argname=None, legacy=False, **kw
|
||||
):
|
||||
if isinstance(original_element, roles.JoinTargetRole):
|
||||
# note that this codepath no longer occurs as of
|
||||
# #6550, unless JoinTargetImpl._skip_clauseelement_for_target_match
|
||||
# were set to False.
|
||||
return original_element
|
||||
elif legacy and isinstance(resolved, str):
|
||||
util.warn_deprecated_20(
|
||||
|
||||
@@ -175,6 +175,10 @@ class BindIntegrationTest(_fixtures.FixtureTest):
|
||||
},
|
||||
"e1",
|
||||
),
|
||||
(
|
||||
lambda users, User: {"clause": select(users).join(User.addresses)},
|
||||
"e1",
|
||||
),
|
||||
(lambda Address: {"mapper": Address}, "e2"),
|
||||
(lambda Address: {"clause": Query([Address])._statement_20()}, "e2"),
|
||||
(lambda addresses: {"clause": select(addresses)}, "e2"),
|
||||
@@ -268,6 +272,7 @@ class BindIntegrationTest(_fixtures.FixtureTest):
|
||||
e2=e2,
|
||||
e3=e3,
|
||||
addresses=addresses,
|
||||
users=users,
|
||||
)
|
||||
|
||||
sess = Session(e3)
|
||||
|
||||
@@ -6,6 +6,7 @@ from sqlalchemy import desc
|
||||
from sqlalchemy import exc as sa_exc
|
||||
from sqlalchemy import ForeignKey
|
||||
from sqlalchemy import func
|
||||
from sqlalchemy import inspect
|
||||
from sqlalchemy import Integer
|
||||
from sqlalchemy import lateral
|
||||
from sqlalchemy import literal_column
|
||||
@@ -307,6 +308,25 @@ class JoinTest(QueryTest, AssertsCompiledSQL):
|
||||
"WHERE users.name = :name_1",
|
||||
)
|
||||
|
||||
def test_join_relationship_propagate_attrs(self):
|
||||
"""test #6558"""
|
||||
|
||||
User = self.classes.User
|
||||
users = self.tables.users
|
||||
|
||||
stmt = select(users).join(User.addresses)
|
||||
|
||||
eq_(
|
||||
stmt._propagate_attrs,
|
||||
{"compile_state_plugin": "orm", "plugin_subject": inspect(User)},
|
||||
)
|
||||
|
||||
self.assert_compile(
|
||||
stmt,
|
||||
"SELECT users.id, users.name FROM users "
|
||||
"JOIN addresses ON users.id = addresses.user_id",
|
||||
)
|
||||
|
||||
def test_invalid_kwarg_join(self):
|
||||
User = self.classes.User
|
||||
sess = fixture_session()
|
||||
|
||||
Reference in New Issue
Block a user