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_2_0_0b3~7 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=7b6259c0f3ae411976f8febfe41f2c5fc3490b13;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 --- 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 8907287383..5f7ff43e42 100644 --- a/lib/sqlalchemy/orm/mapper.py +++ b/lib/sqlalchemy/orm/mapper.py @@ -2908,6 +2908,24 @@ class Mapper( dict(self.class_manager._all_sqla_attributes()) ) + @HasMemoized.memoized_attribute + @util.preload_module("sqlalchemy.orm.descriptor_props") + def _pk_synonyms(self) -> Dict[str, str]: + """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) -> util.ReadOnlyProperties[SynonymProperty[Any]]: diff --git a/lib/sqlalchemy/orm/session.py b/lib/sqlalchemy/orm/session.py index 540e64e7d3..849d37ab22 100644 --- a/lib/sqlalchemy/orm/session.py +++ b/lib/sqlalchemy/orm/session.py @@ -3399,6 +3399,21 @@ class Session(_SessionClassMethods, EventTarget): ) 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] @@ -3409,7 +3424,7 @@ class Session(_SessionClassMethods, EventTarget): raise 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 c05fdaf4fc..dcf7735cd5 100644 --- a/test/orm/test_query.py +++ b/test/orm/test_query.py @@ -1226,6 +1226,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