]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
ensure MappedColumn col is mapped under alternate key
authorMike Bayer <mike_mp@zzzcomputing.com>
Mon, 16 May 2022 02:24:40 +0000 (22:24 -0400)
committerMike Bayer <mike_mp@zzzcomputing.com>
Mon, 16 May 2022 02:24:40 +0000 (22:24 -0400)
Fixes: #8025
Change-Id: I83ba54f05bd2e5fc87d80f42fbeb6d4d2f2ac5fa

lib/sqlalchemy/orm/decl_base.py
test/orm/declarative/test_typed_mapping.py

index b1f81cb6b8e3781d60ed4e4f01e28c1ea68429d0..fe5a20d04da36fc61dda153540877df416848059 100644 (file)
@@ -876,20 +876,27 @@ class _ClassScanMapperConfig(_MapperConfig):
         name_to_prop_key = collections.defaultdict(set)
         for key, c in list(our_stuff.items()):
             if isinstance(c, _MapsColumns):
-                for col in c.columns_to_assign:
-                    if not isinstance(c, Composite):
-                        name_to_prop_key[col.name].add(key)
-                    declared_columns.add(col)
 
-                # remove object from the dictionary that will be passed
-                # as mapper(properties={...}) if it is not a MapperProperty
-                # (i.e. this currently means it's a MappedColumn)
                 mp_to_assign = c.mapper_property_to_assign
                 if mp_to_assign:
                     our_stuff[key] = mp_to_assign
                 else:
+                    # if no mapper property to assign, this currently means
+                    # this is a MappedColumn that will produce a Column for us
                     del our_stuff[key]
 
+                for col in c.columns_to_assign:
+                    if not isinstance(c, Composite):
+                        name_to_prop_key[col.name].add(key)
+                    declared_columns.add(col)
+
+                    # if this is a MappedColumn and the attribute key we
+                    # have is not what the column has for its key, map the
+                    # Column explicitly under the attribute key name.
+                    # otherwise, Mapper will map it under the column key.
+                    if mp_to_assign is None and key != col.key:
+                        our_stuff[key] = col
+
             elif isinstance(c, Column):
                 # undefer previously occurred here, and now occurs earlier.
                 # ensure every column we get here has been named
index 2c9cc3b21bc2b63f7c1c8c9a953a7c256281593d..4e36469d83c5a5bbc569f44f9af69e7ac3ee669f 100644 (file)
@@ -110,6 +110,36 @@ class MappedColumnTest(fixtures.TestBase, testing.AssertsCompiledSQL):
                 id: Mapped[int] = mapped_column(primary_key=True)
                 data = mapped_column()
 
+    @testing.combinations("key", "name", "both", argnames="case")
+    @testing.combinations(True, False, argnames="deferred")
+    @testing.combinations(True, False, argnames="use_add_property")
+    def test_separate_name(self, decl_base, case, deferred, use_add_property):
+        if case == "key":
+            args = {"key": "data_"}
+        elif case == "name":
+            args = {"name": "data_"}
+        else:
+            args = {"name": "data_", "key": "data_"}
+
+        if deferred:
+            args["deferred"] = True
+
+        class A(decl_base):
+            __tablename__ = "a"
+
+            id: Mapped[int] = mapped_column(primary_key=True)
+
+            if not use_add_property:
+                data: Mapped[str] = mapped_column(**args)
+
+        if use_add_property:
+            args["type_"] = String()
+            A.data = mapped_column(**args)
+
+        assert not hasattr(A, "data_")
+        is_(A.data.property.expression, A.__table__.c.data_)
+        eq_(A.__table__.c.data_.key, "data_")
+
     def test_construct_rhs(self, decl_base):
         class User(decl_base):
             __tablename__ = "users"