From: Mike Bayer Date: Fri, 4 Nov 2022 16:48:43 +0000 (-0400) Subject: resolve synonyms in dictionary form of Session.get() X-Git-Tag: rel_1_4_43~4 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=b0885d58328137d274a2872aa47d041e1de553d6;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git resolve synonyms in dictionary form of Session.get() Improved "dictionary mode" for :meth:`_orm.Session.get` so that synonym names which refer to primary key attribute names may be indicated in the named dictionary. Fixes: #8753 Change-Id: I56112564a5c23b51b26e01c64087cbf4399cd951 (cherry picked from commit 7b6259c0f3ae411976f8febfe41f2c5fc3490b13) --- diff --git a/doc/build/changelog/unreleased_14/8753.rst b/doc/build/changelog/unreleased_14/8753.rst new file mode 100644 index 0000000000..6f898e9a06 --- /dev/null +++ b/doc/build/changelog/unreleased_14/8753.rst @@ -0,0 +1,7 @@ +.. change:: + :tags: bug, orm + :tickets: 8753 + + Improved "dictionary mode" for :meth:`_orm.Session.get` so that synonym + names which refer to primary key attribute names may be indicated in the + named dictionary. diff --git a/lib/sqlalchemy/orm/mapper.py b/lib/sqlalchemy/orm/mapper.py index 97509515b8..48f9031430 100644 --- a/lib/sqlalchemy/orm/mapper.py +++ b/lib/sqlalchemy/orm/mapper.py @@ -2575,6 +2575,24 @@ class Mapper( dict(self.class_manager._all_sqla_attributes()) ) + @HasMemoized.memoized_attribute + @util.preload_module("sqlalchemy.orm.descriptor_props") + def _pk_synonyms(self): + """return a dictionary of {syn_attribute_name: pk_attr_name} for + all synonyms that refer to primary key columns + + """ + descriptor_props = util.preloaded.orm_descriptor_props + + pk_keys = {prop.key for prop in self._identity_key_props} + + return { + syn.key: syn.name + for k, syn in self._props.items() + if isinstance(syn, descriptor_props.SynonymProperty) + and syn.name in pk_keys + } + @HasMemoized.memoized_attribute @util.preload_module("sqlalchemy.orm.descriptor_props") def synonyms(self): diff --git a/lib/sqlalchemy/orm/session.py b/lib/sqlalchemy/orm/session.py index ac02870d92..7d32362b50 100644 --- a/lib/sqlalchemy/orm/session.py +++ b/lib/sqlalchemy/orm/session.py @@ -2893,6 +2893,21 @@ class Session(_SessionClassMethods): ) if is_dict: + + pk_synonyms = mapper._pk_synonyms + + if pk_synonyms: + correct_keys = set(pk_synonyms).intersection( + primary_key_identity + ) + + if correct_keys: + primary_key_identity = dict(primary_key_identity) + for k in correct_keys: + primary_key_identity[ + pk_synonyms[k] + ] = primary_key_identity[k] + try: primary_key_identity = list( primary_key_identity[prop.key] @@ -2904,7 +2919,7 @@ class Session(_SessionClassMethods): sa_exc.InvalidRequestError( "Incorrect names of values in identifier to formulate " "primary key for session.get(); primary key attribute " - "names are %s" + "names are %s (synonym names are also accepted)" % ",".join( "'%s'" % prop.key for prop in mapper._identity_key_props diff --git a/test/orm/test_query.py b/test/orm/test_query.py index 2a0eb89265..a7fbf069ec 100644 --- a/test/orm/test_query.py +++ b/test/orm/test_query.py @@ -1210,6 +1210,31 @@ class GetTest(QueryTest): u2 = s.get(User, 7) assert u is not u2 + def test_get_synonym_direct_name(self, decl_base): + """test #8753""" + + class MyUser(decl_base): + __table__ = self.tables.users + + syn_id = synonym("id") + + s = fixture_session() + u = s.get(MyUser, {"syn_id": 7}) + eq_(u.id, 7) + + def test_get_synonym_indirect(self, decl_base): + """test #8753""" + + class MyUser(decl_base): + __table__ = self.tables.users + + uid = __table__.c.id + syn_id = synonym("uid") + + s = fixture_session() + u = s.get(MyUser, {"syn_id": 7}) + eq_(u.uid, 7) + def test_get_composite_pk_no_result(self): CompositePk = self.classes.CompositePk