From c05d3497aa3c6f8446e09c4201f270a0dc0626b8 Mon Sep 17 00:00:00 2001 From: Mike Bayer Date: Sun, 15 May 2022 22:24:40 -0400 Subject: [PATCH] ensure MappedColumn col is mapped under alternate key Fixes: #8025 Change-Id: I83ba54f05bd2e5fc87d80f42fbeb6d4d2f2ac5fa --- lib/sqlalchemy/orm/decl_base.py | 21 ++++++++++----- test/orm/declarative/test_typed_mapping.py | 30 ++++++++++++++++++++++ 2 files changed, 44 insertions(+), 7 deletions(-) diff --git a/lib/sqlalchemy/orm/decl_base.py b/lib/sqlalchemy/orm/decl_base.py index b1f81cb6b8..fe5a20d04d 100644 --- a/lib/sqlalchemy/orm/decl_base.py +++ b/lib/sqlalchemy/orm/decl_base.py @@ -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 diff --git a/test/orm/declarative/test_typed_mapping.py b/test/orm/declarative/test_typed_mapping.py index 2c9cc3b21b..4e36469d83 100644 --- a/test/orm/declarative/test_typed_mapping.py +++ b/test/orm/declarative/test_typed_mapping.py @@ -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" -- 2.47.2