]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
Make discriminator column used by ConcreteBase configurable
authorJesse Bakker <github@jessebakker.com>
Mon, 17 Aug 2020 14:35:35 +0000 (10:35 -0400)
committerMike Bayer <mike_mp@zzzcomputing.com>
Mon, 17 Aug 2020 15:42:47 +0000 (11:42 -0400)
The name of the virtual column used when using the
:class:`_declarative.AbstractConcreteBase` and
:class:`_declarative.ConcreteBase` classes can now be customized, to allow
for models that have a column that is actually named ``type``.  Pull
request courtesy Jesse-Bakker.

Fixes: #5513
Closes: #5514
Pull-request: https://github.com/sqlalchemy/sqlalchemy/pull/5514
Pull-request-sha: 5e7429f3531e2e22fffe996c9760905578d16ef9

Change-Id: I733737844d4f4e1f52dd2475a66c7044ff7292f5

doc/build/changelog/unreleased_13/5513.rst [new file with mode: 0644]
lib/sqlalchemy/ext/declarative/api.py
test/ext/declarative/test_inheritance.py

diff --git a/doc/build/changelog/unreleased_13/5513.rst b/doc/build/changelog/unreleased_13/5513.rst
new file mode 100644 (file)
index 0000000..50e7404
--- /dev/null
@@ -0,0 +1,9 @@
+.. change::
+    :tags: usecase, declarative, orm
+    :tickets: 5513
+
+    The name of the virtual column used when using the
+    :class:`_declarative.AbstractConcreteBase` and
+    :class:`_declarative.ConcreteBase` classes can now be customized, to allow
+    for models that have a column that is actually named ``type``.  Pull
+    request courtesy Jesse-Bakker.
index cb3ec3cdbdf210cfecde2e210e145b0575cdc726..076fa11206b9073ae324f6b81ff44297219dc8b3 100644 (file)
@@ -15,6 +15,7 @@ from .base import _as_declarative
 from .base import _declarative_constructor
 from .base import _DeferredMapperConfig
 from .base import _del_attribute
+from .base import _get_immediate_cls_attr
 from .clsregistry import _class_resolver
 from ... import exc
 from ... import inspection
@@ -438,6 +439,20 @@ class ConcreteBase(object):
                             'polymorphic_identity':'manager',
                             'concrete':True}
 
+
+    The name of the discriminator column used by :func:`.polymorphic_union`
+    defaults to the name ``type``.  To suit the use case of a mapping where an
+    actual column in a mapped table is already named ``type``, the
+    discriminator name can be configured by setting the
+    ``_concrete_discriminator_name`` attribute::
+
+        class Employee(ConcreteBase, Base):
+            _concrete_discriminator_name = '_concrete_discriminator'
+
+    .. versionadded:: 1.3.19 Added the ``_concrete_discriminator_name``
+       attribute to :class:`_declarative.ConcreteBase` so that the
+       virtual discriminator column name can be customized.
+
     .. seealso::
 
         :class:`.AbstractConcreteBase`
@@ -448,12 +463,12 @@ class ConcreteBase(object):
     """
 
     @classmethod
-    def _create_polymorphic_union(cls, mappers):
+    def _create_polymorphic_union(cls, mappers, discriminator_name):
         return polymorphic_union(
             OrderedDict(
                 (mp.polymorphic_identity, mp.local_table) for mp in mappers
             ),
-            "type",
+            discriminator_name,
             "pjoin",
         )
 
@@ -463,10 +478,15 @@ class ConcreteBase(object):
         if m.with_polymorphic:
             return
 
+        discriminator_name = (
+            _get_immediate_cls_attr(cls, "_concrete_discriminator_name")
+            or "type"
+        )
+
         mappers = list(m.self_and_descendants)
-        pjoin = cls._create_polymorphic_union(mappers)
+        pjoin = cls._create_polymorphic_union(mappers, discriminator_name)
         m._set_with_polymorphic(("*", pjoin))
-        m._set_polymorphic_on(pjoin.c.type)
+        m._set_polymorphic_on(pjoin.c[discriminator_name])
 
 
 class AbstractConcreteBase(ConcreteBase):
@@ -609,7 +629,12 @@ class AbstractConcreteBase(ConcreteBase):
             mn = _mapper_or_none(klass)
             if mn is not None:
                 mappers.append(mn)
-        pjoin = cls._create_polymorphic_union(mappers)
+
+        discriminator_name = (
+            _get_immediate_cls_attr(cls, "_concrete_discriminator_name")
+            or "type"
+        )
+        pjoin = cls._create_polymorphic_union(mappers, discriminator_name)
 
         # For columns that were declared on the class, these
         # are normally ignored with the "__no_table__" mapping,
@@ -628,7 +653,7 @@ class AbstractConcreteBase(ConcreteBase):
 
         def mapper_args():
             args = m_args()
-            args["polymorphic_on"] = pjoin.c.type
+            args["polymorphic_on"] = pjoin.c[discriminator_name]
             return args
 
         to_map.mapper_args_fn = mapper_args
index d33dbd4bee9b6f68d2dd029d674cd626f7a5d35c..4a4e8a00f5dfba96656d9a3b592e3124545cd916 100644 (file)
@@ -2038,3 +2038,38 @@ class ConcreteExtensionConfigTest(
             "(SELECT offers.documenttype AS documenttype, offers.id AS id, "
             "'offer' AS type FROM offers) AS pjoin",
         )
+
+    def test_configure_discriminator_col(self):
+        """test #5513"""
+
+        class Employee(AbstractConcreteBase, Base):
+            _concrete_discriminator_name = "_alt_discriminator"
+            employee_id = Column(Integer, primary_key=True)
+
+        class Manager(Employee):
+            __tablename__ = "manager"
+
+            __mapper_args__ = {
+                "polymorphic_identity": "manager",
+                "concrete": True,
+            }
+
+        class Engineer(Employee):
+            __tablename__ = "engineer"
+
+            __mapper_args__ = {
+                "polymorphic_identity": "engineer",
+                "concrete": True,
+            }
+
+        configure_mappers()
+        self.assert_compile(
+            Session().query(Employee),
+            "SELECT pjoin.employee_id AS pjoin_employee_id, "
+            "pjoin._alt_discriminator AS pjoin__alt_discriminator "
+            "FROM (SELECT engineer.employee_id AS employee_id, "
+            "'engineer' AS _alt_discriminator FROM engineer "
+            "UNION ALL SELECT manager.employee_id AS employee_id, "
+            "'manager' AS _alt_discriminator "
+            "FROM manager) AS pjoin",
+        )