summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJesse Bakker <github@jessebakker.com>2020-08-17 10:35:35 -0400
committerMike Bayer <mike_mp@zzzcomputing.com>2020-08-17 11:42:47 -0400
commitdb2b2e21d6458d29a156a8532a52425333e0317a (patch)
treeb456310331fd094075fb6723114bc80f7f1dd853
parent677f9bd309b825bd3e253954e38ba642f92fef29 (diff)
downloadsqlalchemy-db2b2e21d6458d29a156a8532a52425333e0317a.tar.gz
Make discriminator column used by ConcreteBase configurable
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
-rw-r--r--doc/build/changelog/unreleased_13/5513.rst9
-rw-r--r--lib/sqlalchemy/ext/declarative/api.py37
-rw-r--r--test/ext/declarative/test_inheritance.py35
3 files changed, 75 insertions, 6 deletions
diff --git a/doc/build/changelog/unreleased_13/5513.rst b/doc/build/changelog/unreleased_13/5513.rst
new file mode 100644
index 000000000..50e74046b
--- /dev/null
+++ b/doc/build/changelog/unreleased_13/5513.rst
@@ -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.
diff --git a/lib/sqlalchemy/ext/declarative/api.py b/lib/sqlalchemy/ext/declarative/api.py
index cb3ec3cdb..076fa1120 100644
--- a/lib/sqlalchemy/ext/declarative/api.py
+++ b/lib/sqlalchemy/ext/declarative/api.py
@@ -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
diff --git a/test/ext/declarative/test_inheritance.py b/test/ext/declarative/test_inheritance.py
index d33dbd4be..4a4e8a00f 100644
--- a/test/ext/declarative/test_inheritance.py
+++ b/test/ext/declarative/test_inheritance.py
@@ -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",
+ )