summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormike bayer <mike_mp@zzzcomputing.com>2023-02-06 17:01:50 +0000
committerGerrit Code Review <gerrit@bbpush.zzzcomputing.com>2023-02-06 17:01:50 +0000
commit3c46f2cbab092135f2ca7c63ca7e47cf1a7f0a9c (patch)
tree44cdf485bbd1c3a54fd6b949e581e9004eba900b
parenta7434ecb339554ba095ddddf1978f8606633299c (diff)
parentb927d95d075c4cdf027f784e759c9904416fb73a (diff)
downloadsqlalchemy-3c46f2cbab092135f2ca7c63ca7e47cf1a7f0a9c.tar.gz
Merge "check for superclasses of user defined init" into main
-rw-r--r--doc/build/changelog/unreleased_20/9249.rst13
-rw-r--r--lib/sqlalchemy/orm/decl_api.py2
-rw-r--r--test/orm/declarative/test_basic.py23
3 files changed, 35 insertions, 3 deletions
diff --git a/doc/build/changelog/unreleased_20/9249.rst b/doc/build/changelog/unreleased_20/9249.rst
new file mode 100644
index 000000000..1ffd63a01
--- /dev/null
+++ b/doc/build/changelog/unreleased_20/9249.rst
@@ -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.
diff --git a/lib/sqlalchemy/orm/decl_api.py b/lib/sqlalchemy/orm/decl_api.py
index e3e2611da..5f2b3c696 100644
--- a/lib/sqlalchemy/orm/decl_api.py
+++ b/lib/sqlalchemy/orm/decl_api.py
@@ -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
diff --git a/test/orm/declarative/test_basic.py b/test/orm/declarative/test_basic.py
index 45f0d4200..d4890a781 100644
--- a/test/orm/declarative/test_basic.py
+++ b/test/orm/declarative/test_basic.py
@@ -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: