--- /dev/null
+.. change::
+ :tags: bug, orm
+ :tickets: 7579
+
+ Fixed issue where calling upon :meth:`_orm.regsitry.map_imperatively` more
+ than once for the same class would produce an unexpected error, rather than
+ an informative error that the target class is already mapped. This behavior
+ differed from that of the :func:`_orm.mapper` function which does report an
+ informative message already.
def _add_manager(self, manager):
self._managers[manager] = True
- assert manager.registry is None
+ if manager.registry is not None and manager.is_mapped:
+ raise exc.ArgumentError(
+ "Class '%s' already has a primary mapper defined. "
+ % manager.class_
+ )
manager.registry = self
def configure(self, cascade=False):
if manager is not None:
assert manager.class_ is self.class_
if manager.is_mapped:
+ # changed in #7579:
+ # this message is defined in two places as of this change,
+ # also in decl_api -> _add_manager(). in 2.0, this codepath
+ # is removed as any calls to mapper() / Mapper without
+ # the registry setting up first will be rejected.
raise sa_exc.ArgumentError(
"Class '%s' already has a primary mapper defined. "
% self.class_
from sqlalchemy.orm import dynamic_loader
from sqlalchemy.orm import Load
from sqlalchemy.orm import load_only
+from sqlalchemy.orm import Mapper
+from sqlalchemy.orm import mapper
from sqlalchemy.orm import reconstructor
from sqlalchemy.orm import registry
from sqlalchemy.orm import relationship
from sqlalchemy.testing import assert_raises_message
from sqlalchemy.testing import AssertsCompiledSQL
from sqlalchemy.testing import eq_
+from sqlalchemy.testing import expect_deprecated_20
from sqlalchemy.testing import expect_raises_message
from sqlalchemy.testing import fixtures
from sqlalchemy.testing import is_
foobar="x",
)
+ def test_class_already_mapped(self):
+ users, User = (
+ self.tables.users,
+ self.classes.User,
+ )
+
+ self.mapper(User, users)
+
+ with expect_raises_message(
+ sa.exc.ArgumentError,
+ "Class .*User.* already has a primary mapper defined",
+ ):
+ self.mapper(User, users)
+
+ @testing.combinations(mapper, Mapper)
+ def test_class_already_mapped_legacy(self, fn):
+ users, User = (
+ self.tables.users,
+ self.classes.User,
+ )
+
+ with expect_deprecated_20(
+ r"Calling the mapper\(\) function directly outside"
+ ):
+ fn(User, users)
+
+ with expect_raises_message(
+ sa.exc.ArgumentError,
+ "Class .*User.* already has a primary mapper defined",
+ ):
+ fn(User, users)
+
def test_prop_shadow(self):
"""A backref name may not shadow an existing property name."""