]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
resolve synonyms in dictionary form of Session.get()
authorMike Bayer <mike_mp@zzzcomputing.com>
Fri, 4 Nov 2022 16:48:43 +0000 (12:48 -0400)
committerMike Bayer <mike_mp@zzzcomputing.com>
Fri, 4 Nov 2022 16:51:42 +0000 (12:51 -0400)
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)

doc/build/changelog/unreleased_14/8753.rst [new file with mode: 0644]
lib/sqlalchemy/orm/mapper.py
lib/sqlalchemy/orm/session.py
test/orm/test_query.py

diff --git a/doc/build/changelog/unreleased_14/8753.rst b/doc/build/changelog/unreleased_14/8753.rst
new file mode 100644 (file)
index 0000000..6f898e9
--- /dev/null
@@ -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.
index 97509515b8ae3fd3f99b4a4f7ac2f5d13b1def89..48f9031430f2813465ff22762e36076b56b3ed1f 100644 (file)
@@ -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):
index ac02870d92ef112c69a9b9b4932657d69cf16e18..7d32362b50e92f9b1ca81e346cf39be0eee04cab 100644 (file)
@@ -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
index 2a0eb8926567e27554cd4d78fcc809b6f4e50329..a7fbf069ecf1779aad47ada10198ae46802deb4e 100644 (file)
@@ -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