]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
Guard against wrong dataclass mapping
authorFederico Caselli <cfederico87@gmail.com>
Wed, 1 Feb 2023 21:25:03 +0000 (22:25 +0100)
committerMike Bayer <mike_mp@zzzcomputing.com>
Sun, 5 Feb 2023 16:35:32 +0000 (11:35 -0500)
Ensure that the decorator style @registry.mapped_as_dataclass and
MappedAsDataclass are not mixed.

Fixes: #9211
Change-Id: I5cd94cae862122e4f627d0d051495b3789cf6de5

doc/build/changelog/unreleased_20/9211.rst [new file with mode: 0644]
lib/sqlalchemy/orm/decl_base.py
test/orm/declarative/test_dc_transforms.py

diff --git a/doc/build/changelog/unreleased_20/9211.rst b/doc/build/changelog/unreleased_20/9211.rst
new file mode 100644 (file)
index 0000000..f15a64b
--- /dev/null
@@ -0,0 +1,10 @@
+.. change::
+    :tags: bug, orm
+    :tickets: 9211
+
+    An explicit error is raised if a mapping attempts to mix the use of
+    :class:`_orm.MappedAsDataclass` with
+    :meth:`_orm.registry.mapped_as_dataclass` within the same class hierarchy,
+    as this produces issues with the dataclass function being applied at the
+    wrong time to the mapped class, leading to errors during the mapping
+    process.
index a858f12cb947c6c1adb564453e0c603537b45a61..8ce9d59d0b95e883b8141e6cdda58d73ebf35d77 100644 (file)
@@ -1036,6 +1036,17 @@ class _ClassScanMapperConfig(_MapperConfig):
         if not dataclass_setup_arguments:
             return
 
+        # can't use is_dataclass since it uses hasattr
+        if "__dataclass_fields__" in self.cls.__dict__:
+            raise exc.InvalidRequestError(
+                f"Class {self.cls} is already a dataclass; ensure that "
+                "base classes / decorator styles of establishing dataclasses "
+                "are not being mixed. "
+                "This can happen if a class that inherits from "
+                "'MappedAsDataclass', even indirectly, is been mapped with "
+                "'@registry.mapped_as_dataclass'"
+            )
+
         manager = instrumentation.manager_of_class(self.cls)
         assert manager is not None
 
index 63450f4a170f0e510717f807aff26055023f7ce7..302587b9ace91554ccbdd1a8e34d75a9be174d73 100644 (file)
@@ -629,6 +629,23 @@ class DCTransformsTest(AssertsCompiledSQL, fixtures.TestBase):
         b1 = Bar(mixin_value=5)
         eq_(b1.bar_value, 78)
 
+    def test_mixing_MappedAsDataclass_with_decorator_raises(self, registry):
+        """test #9211"""
+
+        class Mixin(MappedAsDataclass):
+            id: Mapped[int] = mapped_column(primary_key=True, init=False)
+
+        with expect_raises_message(
+            exc.InvalidRequestError,
+            "Class .*Foo.* is already a dataclass; ensure that "
+            "base classes / decorator styles of establishing dataclasses "
+            "are not being mixed. ",
+        ):
+
+            @registry.mapped_as_dataclass
+            class Foo(Mixin):
+                bar_value: Mapped[float] = mapped_column(default=78)
+
 
 class RelationshipDefaultFactoryTest(fixtures.TestBase):
     def test_list(self, dc_decl_base: Type[MappedAsDataclass]):