summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authormike bayer <mike_mp@zzzcomputing.com>2021-02-26 15:28:30 +0000
committerGerrit Code Review <gerrit@ci3.zzzcomputing.com>2021-02-26 15:28:30 +0000
commitc660f496a9611aca81606eaa73449cd069b88455 (patch)
treeb9cf9eced4e6a42903fc4c165d16bb72f18cc0dd
parenta8678d660af78a93e08585540e7a23e0b4229ff1 (diff)
parent638ec10920268b0332c1bf1bb2bf1181979b3f9c (diff)
downloadsqlalchemy-c660f496a9611aca81606eaa73449cd069b88455.tar.gz
Merge "document declarative base made non-dynamically"
-rw-r--r--doc/build/orm/mapping_styles.rst24
-rw-r--r--lib/sqlalchemy/orm/__init__.py1
-rw-r--r--lib/sqlalchemy/orm/decl_api.py14
-rw-r--r--test/orm/declarative/test_basic.py17
4 files changed, 54 insertions, 2 deletions
diff --git a/doc/build/orm/mapping_styles.rst b/doc/build/orm/mapping_styles.rst
index 3abcafed3..622159bd7 100644
--- a/doc/build/orm/mapping_styles.rst
+++ b/doc/build/orm/mapping_styles.rst
@@ -102,6 +102,30 @@ Documentation for Declarative mapping continues at :ref:`declarative_config_topl
:ref:`declarative_config_toplevel`
+.. _orm_explicit_declarative_base:
+
+Creating an Explicit Base Non-Dynamically (for use with mypy, similar)
+----------------------------------------------------------------------
+
+Tools like mypy are not necessarily compatible with the dynamically
+generated ``Base`` delivered by SQLAlchemy functions like :func:`_orm.declarative_base`.
+To build a declarative base in a non-dynamic fashion, the
+:class:`_orm.DeclarativeMeta` class may be used directly as follows::
+
+ from sqlalchemy.orm import registry
+ from sqlalchemy.orm.decl_api import DeclarativeMeta
+
+ mapper_registry = registry()
+
+ class Base(metaclass=DeclarativeMeta):
+ __abstract__ = True
+ registry = mapper_registry
+ metadata = mapper_registry.metadata
+
+The above ``Base`` is equivalent to one created using the
+:meth:`_orm.registry.generate_base` method and will be fully understood by
+type analysis tools without the use of plugins.
+
.. _orm_declarative_decorator:
diff --git a/lib/sqlalchemy/orm/__init__.py b/lib/sqlalchemy/orm/__init__.py
index 4793fc638..ac06efba6 100644
--- a/lib/sqlalchemy/orm/__init__.py
+++ b/lib/sqlalchemy/orm/__init__.py
@@ -22,6 +22,7 @@ from .attributes import QueryableAttribute # noqa
from .context import QueryContext # noqa
from .decl_api import as_declarative # noqa
from .decl_api import declarative_base # noqa
+from .decl_api import DeclarativeMeta # noqa
from .decl_api import declared_attr # noqa
from .decl_api import has_inherited_table # noqa
from .decl_api import registry # noqa
diff --git a/lib/sqlalchemy/orm/decl_api.py b/lib/sqlalchemy/orm/decl_api.py
index 8afdb3a50..1166d307e 100644
--- a/lib/sqlalchemy/orm/decl_api.py
+++ b/lib/sqlalchemy/orm/decl_api.py
@@ -657,11 +657,23 @@ class registry(object):
__tablename__ = "my_table"
id = Column(Integer, primary_key=True)
+ The above dynamically generated class is equivalent to the
+ non-dynamic example below::
+
+ from sqlalchemy.orm import registry
+ from sqlalchemy.orm.decl_api import DeclarativeMeta
+
+ mapper_registry = registry()
+
+ class Base(metaclass=DeclarativeMeta):
+ __abstract__ = True
+ registry = mapper_registry
+ metadata = mapper_registry.metadata
+
The :meth:`_orm.registry.generate_base` method provides the
implementation for the :func:`_orm.declarative_base` function, which
creates the :class:`_orm.registry` and base class all at once.
-
See the section :ref:`orm_declarative_mapping` for background and
examples.
diff --git a/test/orm/declarative/test_basic.py b/test/orm/declarative/test_basic.py
index c779d214c..7fbd14b47 100644
--- a/test/orm/declarative/test_basic.py
+++ b/test/orm/declarative/test_basic.py
@@ -61,9 +61,20 @@ class DeclarativeTestBase(
):
__dialect__ = "default"
+ base_style = "dynamic"
+
def setup_test(self):
global Base
- Base = declarative_base(testing.db)
+
+ if self.base_style == "dynamic":
+ Base = declarative_base(testing.db)
+ elif self.base_style == "explicit":
+ mapper_registry = registry(_bind=testing.db)
+
+ class Base(with_metaclass(DeclarativeMeta)):
+ __abstract__ = True
+ registry = mapper_registry
+ metadata = mapper_registry.metadata
def teardown_test(self):
close_all_sessions()
@@ -71,6 +82,9 @@ class DeclarativeTestBase(
Base.metadata.drop_all(testing.db)
+@testing.combinations(
+ ("dynamic",), ("explicit",), argnames="base_style", id_="s"
+)
class DeclarativeTest(DeclarativeTestBase):
def test_basic(self):
class User(Base, fixtures.ComparableEntity):
@@ -2266,6 +2280,7 @@ class DeclarativeTest(DeclarativeTestBase):
eq_(UserType._set_random_keyword_used_here, True)
+# TODO: this should be using @combinations
def _produce_test(inline, stringbased):
class ExplicitJoinTest(fixtures.MappedTest):
@classmethod