--- /dev/null
+.. change::
+ :tags: bug, orm
+ :tickets: 7545
+
+ A warning is emitted when attempting to configure a mapped class within an
+ inheritance hierarchy where the mapper is not given any polymorphic
+ identity, however there is a polymorphic discriminator column assigned.
+ Such classes should be abstract if they never intend to load directly.
+
else:
self.persist_selectable = self.local_table
- if self.polymorphic_identity is not None and not self.concrete:
- self._identity_class = self.inherits._identity_class
- else:
+ if self.polymorphic_identity is None:
+ self._identity_class = self.class_
+
+ if self.inherits.base_mapper.polymorphic_on is not None:
+ util.warn(
+ "Mapper %s does not indicate a polymorphic_identity, "
+ "yet is part of an inheritance hierarchy that has a "
+ "polymorphic_on column of '%s'. Objects of this type "
+ "cannot be loaded polymorphically which can lead to "
+ "degraded or incorrect loading behavior in some "
+ "scenarios. Please establish a polmorphic_identity "
+ "for this class, or leave it un-mapped. "
+ "To omit mapping an intermediary class when using "
+ "declarative, set the '__abstract__ = True' "
+ "attribute on that class."
+ % (self, self.inherits.base_mapper.polymorphic_on)
+ )
+ elif self.concrete:
self._identity_class = self.class_
+ else:
+ self._identity_class = self.inherits._identity_class
if self.version_id_col is None:
self.version_id_col = self.inherits.version_id_col
status: Mapped[str] = mapped_column(String(30))
engineer_name: Mapped[str]
primary_language: Mapped[str]
+ __mapper_args__ = {"polymorphic_identity": "engineer"}
e1 = Engineer("nm", "st", "en", "pl")
eq_(e1.name, "nm")
status: Mapped[str] = mapped_column(String(30))
engineer_name: Mapped[str]
primary_language: Mapped[str]
+ __mapper_args__ = {"polymorphic_identity": "engineer"}
e1 = Engineer("st", "en", "pl")
eq_(e1.status, "st")
class DeclarativeInheritanceTest(DeclarativeTestBase):
+ @testing.emits_warning(r".*does not indicate a polymorphic_identity")
def test_we_must_copy_mapper_args(self):
class Person(Base):
__tablename__ = "employee"
id = Column(Integer, ForeignKey(Person.id), primary_key=True)
+ __mapper_args__ = {
+ "polymorphic_identity": "employee",
+ }
class Engineer(Employee):
__mapper_args__ = {"polymorphic_identity": "engineer"}
__mapper_args__ = {"polymorphic_identity": "manager"}
id = Column(Integer, ForeignKey("people.id"), primary_key=True)
golf_swing = Column(String(50))
+ __mapper_args__ = {
+ "polymorphic_identity": "manager",
+ }
class Boss(Manager):
boss_name = Column(String(50))
+ __mapper_args__ = {
+ "polymorphic_identity": "boss",
+ }
is_(
Boss.__mapper__.column_attrs["boss_name"].columns[0],
person_id: Mapped[int] = mapped_column(
ForeignKey("person.person_id"), primary_key=True
)
+ __mapper_args__ = {"polymorphic_identity": "engineer"}
status: Mapped[str] = mapped_column(String(30))
engineer_name: Mapped[opt_str50]
)
status: Mapped[str] = mapped_column(String(30))
manager_name: Mapped[str50]
+ __mapper_args__ = {"polymorphic_identity": "manager"}
is_(Person.__mapper__.polymorphic_on, Person.__table__.c.type)
from sqlalchemy.testing import assert_raises
from sqlalchemy.testing import assert_raises_message
from sqlalchemy.testing import eq_
+from sqlalchemy.testing import expect_warnings
from sqlalchemy.testing import fixtures
from sqlalchemy.testing import is_
from sqlalchemy.testing import mock
cls.mapper_registry.map_imperatively(
A, base, polymorphic_on=base.c.type
)
- cls.mapper_registry.map_imperatively(B, inherits=A)
+
+ with expect_warnings(
+ r"Mapper Mapper\[B\(base\)\] does not indicate a "
+ "polymorphic_identity,"
+ ):
+ cls.mapper_registry.map_imperatively(B, inherits=A)
cls.mapper_registry.map_imperatively(
C, inherits=B, polymorphic_identity="c"
)
cls.mapper_registry.map_imperatively(
E, inherits=A, polymorphic_identity="e"
)
+ cls.mapper_registry.configure()
+
+ def test_warning(self, decl_base):
+ """test #7545"""
+
+ class A(decl_base):
+ __tablename__ = "a"
+ id = Column(Integer, primary_key=True)
+ type = Column(String)
+
+ __mapper_args__ = {"polymorphic_on": type}
+
+ class B(A):
+ __mapper_args__ = {"polymorphic_identity": "b"}
+
+ with expect_warnings(
+ r"Mapper Mapper\[C\(a\)\] does not indicate a "
+ "polymorphic_identity,"
+ ):
+
+ class C(A):
+ __mapper_args__ = {}
def test_load_from_middle(self):
C, B = self.classes.C, self.classes.B
# not been loaded yet (Employer), and therefore cannot be configured:
class Mammal(Animal):
nonexistent = relationship("Nonexistent")
+ __mapper_args__ = {"polymorphic_identity": "mammal"}
# These new classes should not be configured at this point:
unconfigured = list(mapperlib._unconfigured_mappers())