From: Mike Bayer Date: Fri, 14 May 2021 14:29:55 +0000 (-0400) Subject: Consult plugin_subject for non-ORM enabled stmts in get_bind() X-Git-Tag: rel_1_4_16~27 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=31b793894f7e45503ebebd28d93fc95d92768aa1;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git Consult plugin_subject for non-ORM enabled stmts in get_bind() Enhanced the bind resolution rules for :meth:`_orm.Session.execute` so that when a non-ORM statement such as an :func:`_sql.insert` construct nonetheless is built against ORM objects, to the greatest degree possible the ORM entity will be used to resolve the bind, such as for a :class:`_orm.Session` that has a bind map set up on a common superclass without specific mappers or tables named in the map. Fixes: #6484 Change-Id: Iaa711b7f2c7451377b50af63029f37c4375a6f7e --- diff --git a/doc/build/changelog/unreleased_14/6484.rst b/doc/build/changelog/unreleased_14/6484.rst new file mode 100644 index 0000000000..28106ddbf4 --- /dev/null +++ b/doc/build/changelog/unreleased_14/6484.rst @@ -0,0 +1,10 @@ +.. change:: + :tags: bug, orm + :tickets: 6484 + + Enhanced the bind resolution rules for :meth:`_orm.Session.execute` so that + when a non-ORM statement such as an :func:`_sql.insert` construct + nonetheless is built against ORM objects, to the greatest degree possible + the ORM entity will be used to resolve the bind, such as for a + :class:`_orm.Session` that has a bind map set up on a common superclass + without specific mappers or tables named in the map. diff --git a/lib/sqlalchemy/orm/session.py b/lib/sqlalchemy/orm/session.py index cdf3a15856..e4a9b90463 100644 --- a/lib/sqlalchemy/orm/session.py +++ b/lib/sqlalchemy/orm/session.py @@ -1997,6 +1997,15 @@ class Session(_SessionClassMethods): clause = mapper.persist_selectable if clause is not None: + plugin_subject = clause._propagate_attrs.get( + "plugin_subject", None + ) + + if plugin_subject is not None: + for cls in plugin_subject.mapper.class_.__mro__: + if cls in self.__binds: + return self.__binds[cls] + for obj in visitors.iterate(clause): if obj in self.__binds: return self.__binds[obj] diff --git a/test/orm/test_bind.py b/test/orm/test_bind.py index 014fa152e9..5c7f3f72e5 100644 --- a/test/orm/test_bind.py +++ b/test/orm/test_bind.py @@ -1,5 +1,7 @@ import sqlalchemy as sa +from sqlalchemy import delete from sqlalchemy import ForeignKey +from sqlalchemy import insert from sqlalchemy import inspect from sqlalchemy import Integer from sqlalchemy import MetaData @@ -7,6 +9,8 @@ from sqlalchemy import select from sqlalchemy import table from sqlalchemy import testing from sqlalchemy import true +from sqlalchemy import update +from sqlalchemy.orm import aliased from sqlalchemy.orm import backref from sqlalchemy.orm import mapper from sqlalchemy.orm import relationship @@ -734,3 +738,23 @@ class GetBindTest(fixtures.MappedTest): select(self.tables.concrete_sub_table) ) is_(session.get_bind(clause=stmt), base_class_bind) + + @testing.combinations( + (insert,), + (update,), + (delete,), + (select,), + ) + def test_clause_extracts_orm_plugin_subject(self, sql_elem): + ClassWMixin = self.classes.ClassWMixin + MixinOne = self.classes.MixinOne + base_class_bind = Mock() + + session = self._fixture({MixinOne: base_class_bind}) + + stmt = sql_elem(ClassWMixin) + is_(session.get_bind(clause=stmt), base_class_bind) + + cwm_alias = aliased(ClassWMixin) + stmt = sql_elem(cwm_alias) + is_(session.get_bind(clause=stmt), base_class_bind)