Modified the ``__init_subclass__()`` method used by
:class:`_orm.MappedAsDataclass`, :class:`_orm.DeclarativeBase`` and
:class:`_orm.DeclarativeBaseNoMeta` to accept arbitrary ``**kw`` and to
propagate them to the ``super()`` call, allowing greater flexibility in
arranging custom superclasses and mixins which make use of
``__init_subclass__()`` keyword arguments. Pull request courtesy Michael
Oliver.
Fixes: #10732
Closes: #10733
Pull-request: https://github.com/sqlalchemy/sqlalchemy/pull/10733
Pull-request-sha:
7fdeec1f3224f48213c9c9af5f3e7e5d0904dafa
Change-Id: I955a735d4e23502b5a6b22ac093e391b378edc87
--- /dev/null
+.. change::
+ :tags: bug, orm
+ :tickets: 10668
+
+ Modified the ``__init_subclass__()`` method used by
+ :class:`_orm.MappedAsDataclass`, :class:`_orm.DeclarativeBase`` and
+ :class:`_orm.DeclarativeBaseNoMeta` to accept arbitrary ``**kw`` and to
+ propagate them to the ``super()`` call, allowing greater flexibility in
+ arranging custom superclasses and mixins which make use of
+ ``__init_subclass__()`` keyword arguments. Pull request courtesy Michael
+ Oliver.
+
def get_select_precolumns(self, select, **kw):
"""Add special MySQL keywords in place of DISTINCT.
- .. deprecated 1.4:: this usage is deprecated.
+ .. deprecated:: 1.4 This usage is deprecated.
:meth:`_expression.Select.prefix_with` should be used for special
keywords at the start of a SELECT.
dataclass_callable: Union[
_NoArg, Callable[..., Type[Any]]
] = _NoArg.NO_ARG,
+ **kw: Any,
) -> None:
apply_dc_transforms: _DataclassArguments = {
"init": init,
current_transforms
) = apply_dc_transforms
- super().__init_subclass__()
+ super().__init_subclass__(**kw)
if not _is_mapped_class(cls):
new_anno = (
def __init__(self, **kw: Any):
...
- def __init_subclass__(cls) -> None:
+ def __init_subclass__(cls, **kw: Any) -> None:
if DeclarativeBase in cls.__bases__:
_check_not_declarative(cls, DeclarativeBase)
_setup_declarative_base(cls)
else:
_as_declarative(cls._sa_registry, cls, cls.__dict__)
- super().__init_subclass__()
+ super().__init_subclass__(**kw)
def _check_not_declarative(cls: Type[Any], base: Type[Any]) -> None:
def __init__(self, **kw: Any):
...
- def __init_subclass__(cls) -> None:
+ def __init_subclass__(cls, **kw: Any) -> None:
if DeclarativeBaseNoMeta in cls.__bases__:
_check_not_declarative(cls, DeclarativeBaseNoMeta)
_setup_declarative_base(cls)
else:
_as_declarative(cls._sa_registry, cls, cls.__dict__)
+ super().__init_subclass__(**kw)
def add_mapped_attribute(
from sqlalchemy.orm import joinedload
from sqlalchemy.orm import Mapped
from sqlalchemy.orm import mapped_column
+from sqlalchemy.orm import MappedAsDataclass
from sqlalchemy.orm import MappedColumn
from sqlalchemy.orm import Mapper
from sqlalchemy.orm import registry
# Check to see if __init_subclass__ works in supported versions
eq_(UserType._set_random_keyword_used_here, True)
+ @testing.variation(
+ "basetype",
+ ["DeclarativeBase", "DeclarativeBaseNoMeta", "MappedAsDataclass"],
+ )
+ def test_kw_support_in_declarative_base(self, basetype):
+ """test #10732"""
+
+ if basetype.DeclarativeBase:
+
+ class Base(DeclarativeBase):
+ pass
+
+ elif basetype.DeclarativeBaseNoMeta:
+
+ class Base(DeclarativeBaseNoMeta):
+ pass
+
+ elif basetype.MappedAsDataclass:
+
+ class Base(MappedAsDataclass):
+ pass
+
+ else:
+ basetype.fail()
+
+ class Mixin:
+ def __init_subclass__(cls, random_keyword: bool, **kw) -> None:
+ super().__init_subclass__(**kw)
+ cls._set_random_keyword_used_here = random_keyword
+
+ class User(Base, Mixin, random_keyword=True):
+ __tablename__ = "user"
+ id_ = Column(Integer, primary_key=True)
+
+ eq_(User._set_random_keyword_used_here, True)
+
def test_declarative_base_bad_registry(self):
with assertions.expect_raises_message(
exc.InvalidRequestError,