]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
prevent Mapped[] auto-column logic w/ fixed table
authorMike Bayer <mike_mp@zzzcomputing.com>
Wed, 2 Mar 2022 16:51:57 +0000 (11:51 -0500)
committerMike Bayer <mike_mp@zzzcomputing.com>
Wed, 2 Mar 2022 16:51:57 +0000 (11:51 -0500)
When a class has __table__, people will still want to
annotate the attributes on classes, so make sure a
Mapped annotation without a right side is only interpreted
as a column if there is no __table__

Change-Id: I7da4b4c43c4d2c8b6834b781569cb551e75b57b1

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

index 5c72371b29874430195ec0934104f0c6136bee2c..3fb8af80ca9488d6518d2fdd2ca103b98ca4f409 100644 (file)
@@ -498,6 +498,7 @@ class _ClassScanMapperConfig(_MapperConfig):
         mapper_args_fn = None
         table_args = inherited_table_args = None
         tablename = None
+        fixed_table = "__table__" in clsdict_view
 
         attribute_is_overridden = self._cls_attr_override_checker(self.cls)
 
@@ -666,7 +667,8 @@ class _ClassScanMapperConfig(_MapperConfig):
                             is_dataclass,
                         )
                         if obj is None:
-                            collected_attributes[name] = MappedColumn()
+                            if not fixed_table:
+                                collected_attributes[name] = MappedColumn()
                         else:
                             collected_attributes[name] = obj
                     else:
@@ -701,7 +703,11 @@ class _ClassScanMapperConfig(_MapperConfig):
                         annotation,
                         False,
                     )
-                    if obj is None and _is_mapped_annotation(annotation, cls):
+                    if (
+                        obj is None
+                        and not fixed_table
+                        and _is_mapped_annotation(annotation, cls)
+                    ):
                         collected_attributes[name] = MappedColumn()
                     elif name in clsdict_view:
                         collected_attributes[name] = obj
index 71eb7ce42b11063595e4b6144126e9365eac20eb..57ce969ac885dcf9b9920eac271596cc537403c3 100644 (file)
@@ -160,6 +160,39 @@ class MappedColumnTest(fixtures.TestBase, testing.AssertsCompiledSQL):
         is_true(User.__table__.c.data.nullable)
         assert isinstance(User.__table__.c.created_at.type, DateTime)
 
+    def test_anno_w_fixed_table(self, decl_base):
+        users = Table(
+            "users",
+            decl_base.metadata,
+            Column("id", Integer, primary_key=True),
+            Column("name", String(50), nullable=False),
+            Column("data", String(50)),
+            Column("x", Integer),
+            Column("y", Integer),
+            Column("created_at", DateTime),
+        )
+
+        class User(decl_base):
+            __table__ = users
+
+            id: Mapped[int]
+            name: Mapped[str]
+            data: Mapped[Optional[str]]
+            x: Mapped[int]
+            y: Mapped[int]
+            created_at: Mapped[datetime.datetime]
+
+        self.assert_compile(
+            select(User),
+            "SELECT users.id, users.name, users.data, users.x, "
+            "users.y, users.created_at FROM users",
+        )
+        eq_(User.__mapper__.primary_key, (User.__table__.c.id,))
+        is_false(User.__table__.c.id.nullable)
+        is_false(User.__table__.c.name.nullable)
+        is_true(User.__table__.c.data.nullable)
+        assert isinstance(User.__table__.c.created_at.type, DateTime)
+
     def test_construct_lhs_type_missing(self, decl_base):
         class MyClass:
             pass
@@ -421,6 +454,32 @@ class MixinTest(fixtures.TestBase, testing.AssertsCompiledSQL):
         # ordering of cols is TODO
         eq_(A.__table__.c.keys(), ["id", "y", "name", "x"])
 
+        self.assert_compile(select(A), "SELECT a.id, a.y, a.name, a.x FROM a")
+
+    def test_mapped_column_omit_fn_fixed_table(self, decl_base):
+        class MixinOne:
+            name: Mapped[str]
+            x: Mapped[int]
+            y: Mapped[int]
+
+        a = Table(
+            "a",
+            decl_base.metadata,
+            Column("id", Integer, primary_key=True),
+            Column("name", String(50), nullable=False),
+            Column("data", String(50)),
+            Column("x", Integer),
+            Column("y", Integer),
+        )
+
+        class A(MixinOne, decl_base):
+            __table__ = a
+            id: Mapped[int]
+
+        self.assert_compile(
+            select(A), "SELECT a.id, a.name, a.data, a.x, a.y FROM a"
+        )
+
     def test_mc_duplication_plain(self, decl_base):
         class MixinOne:
             name: Mapped[str] = mapped_column()