]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
check for superclasses of user defined init
authorMike Bayer <mike_mp@zzzcomputing.com>
Mon, 6 Feb 2023 15:16:26 +0000 (10:16 -0500)
committerMike Bayer <mike_mp@zzzcomputing.com>
Mon, 6 Feb 2023 15:16:26 +0000 (10:16 -0500)
Fixed regression caused by the fix for :ticket:`9171`, which itself was
fixing a regression, involving the mechanics of ``__init__()`` on classes
that extend from :class:`_orm.DeclarativeBase`. The change made it such
that ``__init__()`` was applied to the user-defined base if there were no
``__init__()`` method directly on the class. This has been adjusted so that
``__init__()`` is applied only if no other class in the hierarchy of the
user-defined base has an ``__init__()`` method. This again allows
user-defined base classes based on :class:`_orm.DeclarativeBase` to include
mixins that themselves include a custom ``__init__()`` method.

Fixes: #9249
Change-Id: I78f32590ce9ffe245eccb4bd5bd7c884d4e015d5

doc/build/changelog/unreleased_20/9249.rst [new file with mode: 0644]
lib/sqlalchemy/orm/decl_api.py
test/orm/declarative/test_basic.py

diff --git a/doc/build/changelog/unreleased_20/9249.rst b/doc/build/changelog/unreleased_20/9249.rst
new file mode 100644 (file)
index 0000000..1ffd63a
--- /dev/null
@@ -0,0 +1,13 @@
+.. change::
+    :tags: bug, orm
+    :tickets: 9249
+
+    Fixed regression caused by the fix for :ticket:`9171`, which itself was
+    fixing a regression, involving the mechanics of ``__init__()`` on classes
+    that extend from :class:`_orm.DeclarativeBase`. The change made it such
+    that ``__init__()`` was applied to the user-defined base if there were no
+    ``__init__()`` method directly on the class. This has been adjusted so that
+    ``__init__()`` is applied only if no other class in the hierarchy of the
+    user-defined base has an ``__init__()`` method. This again allows
+    user-defined base classes based on :class:`_orm.DeclarativeBase` to include
+    mixins that themselves include a custom ``__init__()`` method.
index e3e2611da16ee532e7c3ae7346ab24150a66b38c..5f2b3c6966065df6b12518458bf85a203ea757a8 100644 (file)
@@ -567,7 +567,7 @@ def _setup_declarative_base(cls: Type[Any]) -> None:
     if "metadata" not in cls.__dict__:
         cls.metadata = cls.registry.metadata  # type: ignore
 
-    if "__init__" not in cls.__dict__:
+    if getattr(cls, "__init__", object.__init__) is object.__init__:
         cls.__init__ = cls.registry.constructor
 
 
index 45f0d4200bb9d445c6ab3fb1f2bc790ddcac733d..d4890a7815783c30b40b5da0cafc24ef35f859d3 100644 (file)
@@ -134,7 +134,13 @@ class DeclarativeBaseSetupsTest(fixtures.TestBase):
 
     @testing.variation(
         "base_type",
-        ["declbase", "declbasenometa", "declbasefn", "asdeclarative"],
+        [
+            "declbase",
+            "declbasenometa",
+            "declbasefn",
+            "asdeclarative",
+            "mixinonbase",
+        ],
     )
     def test_reg_constructor_custom_init(self, base_type):
         """test for #9171 testing what an explicit __init__ does.
@@ -165,6 +171,15 @@ class DeclarativeBaseSetupsTest(fixtures.TestBase):
                     m1.init(x)
 
             Base = declarative_base(cls=_B)
+        elif base_type.mixinonbase:
+
+            class Mixin:
+                def __init__(self, x=None):
+                    m1.init(x)
+
+            class Base(Mixin, DeclarativeBase):
+                pass
+
         elif base_type.asdeclarative:
 
             @as_declarative()
@@ -180,7 +195,11 @@ class DeclarativeBaseSetupsTest(fixtures.TestBase):
 
         fs = fakeself()
 
-        if base_type.declbase or base_type.declbasenometa:
+        if (
+            base_type.declbase
+            or base_type.declbasenometa
+            or base_type.mixinonbase
+        ):
             Base.__init__(fs, x=5)
             eq_(m1.mock_calls, [mock.call.init(5)])
         else: