]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
more abstractconcretebase clarity
authorMike Bayer <mike_mp@zzzcomputing.com>
Thu, 18 Aug 2022 15:54:04 +0000 (11:54 -0400)
committerMike Bayer <mike_mp@zzzcomputing.com>
Thu, 18 Aug 2022 15:55:33 +0000 (11:55 -0400)
Change-Id: I9ddb6b1a2e0c0be1fe355a7ea714d0e16aa93b47
(cherry picked from commit a47d76ca25275344345b208def5f72292e8687b4)

doc/build/orm/inheritance.rst
lib/sqlalchemy/ext/declarative/extensions.py

index 27498b375aff6469e51d5452e21b17774b945346..4fd3569be5b2202c419aec8a29e65f2e35525d3e 100644 (file)
@@ -637,7 +637,9 @@ Using :class:`.ConcreteBase`, we can set up our concrete mapping in
 almost the same way as we do other forms of inheritance mappings::
 
     from sqlalchemy.ext.declarative import ConcreteBase
+    from sqlalchemy.orm import declarative_base
 
+    Base = declarative_base()
 
     class Employee(ConcreteBase, Base):
         __tablename__ = "employee"
@@ -723,6 +725,12 @@ The above UNION query needs to manufacture "NULL" columns for each subtable
 in order to accommodate for those columns that aren't members of that
 particular subclass.
 
+.. seealso::
+
+    :class:`.ConcreteBase`
+
+.. _abstract_concrete_base:
+
 Abstract Concrete Classes
 +++++++++++++++++++++++++
 
@@ -737,6 +745,10 @@ tables, and leave the base class unmapped, this can be achieved very easily.
 When using Declarative, just declare the
 base class with the ``__abstract__`` indicator::
 
+    from sqlalchemy.orm import declarative_base
+
+    Base = declarative_base()
+
     class Employee(Base):
         __abstract__ = True
 
@@ -747,10 +759,6 @@ base class with the ``__abstract__`` indicator::
         name = Column(String(50))
         manager_data = Column(String(40))
 
-        __mapper_args__ = {
-            "polymorphic_identity": "manager",
-        }
-
 
     class Engineer(Employee):
         __tablename__ = "engineer"
@@ -758,10 +766,6 @@ base class with the ``__abstract__`` indicator::
         name = Column(String(50))
         engineer_info = Column(String(40))
 
-        __mapper_args__ = {
-            "polymorphic_identity": "engineer",
-        }
-
 Above, we are not actually making use of SQLAlchemy's inheritance mapping
 facilities; we can load and persist instances of ``Manager`` and ``Engineer``
 normally.   The situation changes however when we need to **query polymorphically**,
@@ -781,6 +785,9 @@ To help with this, Declarative offers a variant of the :class:`.ConcreteBase`
 class called :class:`.AbstractConcreteBase` which achieves this automatically::
 
     from sqlalchemy.ext.declarative import AbstractConcreteBase
+    from sqlalchemy.orm import declarative_base
+
+    Base = declarative_base()
 
 
     class Employee(AbstractConcreteBase, Base):
@@ -810,13 +817,22 @@ class called :class:`.AbstractConcreteBase` which achieves this automatically::
             "concrete": True,
         }
 
-The :class:`.AbstractConcreteBase` helper class has a more complex internal
-process than that of :class:`.ConcreteBase`, in that the entire mapping
+    Base.registry.configure()
+
+Above, the :meth:`_orm.registry.configure` method is invoked, which will
+trigger the ``Employee`` class to be actually mapped; before the configuration
+step, the class has no mapping as the sub-tables which it will query from
+have not yet been defined.   This process is more complex than that of
+:class:`.ConcreteBase`, in that the entire mapping
 of the base class must be delayed until all the subclasses have been declared.
 With a mapping like the above, only instances of ``Manager`` and ``Engineer``
 may be persisted; querying against the ``Employee`` class will always produce
 ``Manager`` and ``Engineer`` objects.
 
+.. seealso::
+
+    :class:`.AbstractConcreteBase`
+
 
 Classical and Semi-Classical Concrete Polymorphic Configuration
 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
index 78188416aa3e260cb148376b041f4538a3bfcce4..4f60d8bf2828339eadf53b168871ee1a73e4a754 100644 (file)
@@ -144,8 +144,8 @@ class AbstractConcreteBase(ConcreteBase):
 
     .. note::
 
-        The :class:`.AbstractConcreteBase` class does not intend to set up  the
-        mapping for the base class until all the subclasses have been defined,
+        The :class:`.AbstractConcreteBase` delays the mapper creation of the
+        base class until all the subclasses have been defined,
         as it needs to create a mapping against a selectable that will include
         all subclass tables.  In order to achieve this, it waits for the
         **mapper configuration event** to occur, at which point it scans
@@ -155,22 +155,20 @@ class AbstractConcreteBase(ConcreteBase):
         While this event is normally invoked automatically, in the case of
         :class:`.AbstractConcreteBase`, it may be necessary to invoke it
         explicitly after **all** subclass mappings are defined, if the first
-        operation is to be a query against this base class.  To do so, invoke
-        :func:`.configure_mappers` once all the desired classes have been
-        configured::
-
-            from sqlalchemy.orm import configure_mappers
-
-            configure_mappers()
-
-        .. seealso::
-
-            :func:`_orm.configure_mappers`
+        operation is to be a query against this base class. To do so, once all
+        the desired classes have been configured, the
+        :meth:`_orm.registry.configure` method on the :class:`_orm.registry`
+        in use can be invoked, which is available in relation to a particular
+        declarative base class::
 
+            Base.registry.configure()
 
     Example::
 
         from sqlalchemy.ext.declarative import AbstractConcreteBase
+        from sqlalchemy.orm import declarative_base
+
+        Base = declarative_base()
 
         class Employee(AbstractConcreteBase, Base):
             pass
@@ -183,9 +181,10 @@ class AbstractConcreteBase(ConcreteBase):
 
             __mapper_args__ = {
                 'polymorphic_identity':'manager',
-                'concrete':True}
+                'concrete':True
+            }
 
-        configure_mappers()
+        Base.registry.configure()
 
     The abstract base class is handled by declarative in a special way;
     at class configuration time, it behaves like a declarative mixin
@@ -221,18 +220,17 @@ class AbstractConcreteBase(ConcreteBase):
 
             __mapper_args__ = {
                 'polymorphic_identity':'manager',
-                'concrete':True}
+                'concrete':True
+            }
 
-        configure_mappers()
+        Base.registry.configure()
 
     When we make use of our mappings however, both ``Manager`` and
     ``Employee`` will have an independently usable ``.company`` attribute::
 
-        session.query(Employee).filter(Employee.company.has(id=5))
-
-    .. versionchanged:: 1.0.0 - The mechanics of :class:`.AbstractConcreteBase`
-       have been reworked to support relationships established directly
-       on the abstract base, without any special configurational steps.
+        session.execute(
+            select(Employee).filter(Employee.company.has(id=5))
+        )
 
     .. seealso::
 
@@ -240,6 +238,8 @@ class AbstractConcreteBase(ConcreteBase):
 
         :ref:`concrete_inheritance`
 
+        :ref:`abstract_concrete_base`
+
     """
 
     __no_table__ = True