diff options
| author | Federico Caselli <cfederico87@gmail.com> | 2022-07-15 21:55:52 +0200 |
|---|---|---|
| committer | Federico Caselli <cfederico87@gmail.com> | 2022-07-18 23:34:16 +0200 |
| commit | 5777e268d092889d1764ed13cb62d007cdbeb0b5 (patch) | |
| tree | c2d5cccd724fa647aa7016f5946e9766a8115035 /lib/sqlalchemy/orm | |
| parent | f53011e40d7332e28bb6d309db7829ee31c47d7d (diff) | |
| download | sqlalchemy-5777e268d092889d1764ed13cb62d007cdbeb0b5.tar.gz | |
Ensure that a daclarative base is not used directly
Fixes: #8248
Change-Id: I4f4c690dd8659eaf74e9c757d681e9edc7d33eee
Diffstat (limited to 'lib/sqlalchemy/orm')
| -rw-r--r-- | lib/sqlalchemy/orm/decl_api.py | 34 |
1 files changed, 24 insertions, 10 deletions
diff --git a/lib/sqlalchemy/orm/decl_api.py b/lib/sqlalchemy/orm/decl_api.py index 7249698c0..500f2786e 100644 --- a/lib/sqlalchemy/orm/decl_api.py +++ b/lib/sqlalchemy/orm/decl_api.py @@ -522,7 +522,7 @@ def declarative_mixin(cls: Type[_T]) -> Type[_T]: def _setup_declarative_base(cls: Type[Any]) -> None: if "metadata" in cls.__dict__: - metadata = cls.metadata # type: ignore + metadata = cls.__dict__["metadata"] else: metadata = None @@ -688,11 +688,27 @@ class DeclarativeBase( def __init_subclass__(cls) -> None: if DeclarativeBase in cls.__bases__: + _check_not_declarative(cls, DeclarativeBase) _setup_declarative_base(cls) else: _as_declarative(cls._sa_registry, cls, cls.__dict__) +def _check_not_declarative(cls: Type[Any], base: Type[Any]) -> None: + cls_dict = cls.__dict__ + if ( + "__table__" in cls_dict + and not ( + callable(cls_dict["__table__"]) + or hasattr(cls_dict["__table__"], "__get__") + ) + ) or isinstance(cls_dict.get("__tablename__", None), str): + raise exc.InvalidRequestError( + f"Cannot use {base.__name__!r} directly as a declarative base " + "class. Create a Base by creating a subclass of it." + ) + + class DeclarativeBaseNoMeta(inspection.Inspectable[Mapper[Any]]): """Same as :class:`_orm.DeclarativeBase`, but does not use a metaclass to intercept new attributes. @@ -705,22 +721,20 @@ class DeclarativeBaseNoMeta(inspection.Inspectable[Mapper[Any]]): """ - if typing.TYPE_CHECKING: - registry: ClassVar[_RegistryType] - _sa_registry: ClassVar[_RegistryType] - metadata: ClassVar[MetaData] - - __name__: ClassVar[str] - __mapper__: ClassVar[Mapper[Any]] - __table__: ClassVar[Optional[FromClause]] + registry: ClassVar[_RegistryType] + _sa_registry: ClassVar[_RegistryType] + metadata: ClassVar[MetaData] + __mapper__: ClassVar[Mapper[Any]] + __table__: Optional[FromClause] - __tablename__: ClassVar[Any] + if typing.TYPE_CHECKING: def __init__(self, **kw: Any): ... def __init_subclass__(cls) -> None: if DeclarativeBaseNoMeta in cls.__bases__: + _check_not_declarative(cls, DeclarativeBaseNoMeta) _setup_declarative_base(cls) else: cls._sa_registry.map_declaratively(cls) |
