--- /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
+ if manager.is_mapped:
+ raise exc.ArgumentError(
+ "Class '%s' already has a primary mapper defined. "
+ % manager.class_
+ )
assert manager.registry is None
manager.registry = self
manager.registry._add_non_primary_mapper(self)
return
- if manager is not None:
- assert manager.class_ is self.class_
- if manager.is_mapped:
- raise sa_exc.ArgumentError(
- "Class '%s' already has a primary mapper defined. "
- % self.class_
- )
- # else:
- # a ClassManager may already exist as
- # ClassManager.instrument_attribute() creates
- # new managers for each subclass if they don't yet exist.
+ if manager is None or not manager.registry:
+ raise sa_exc.InvalidRequestError(
+ "The _mapper() function and Mapper() constructor may not be "
+ "invoked directly outside of a declarative registry."
+ " Please use the sqlalchemy.orm.registry.map_imperatively() "
+ "function for a classical mapping."
+ )
self.dispatch.instrument_class(self, self.class_)
finalize=True,
)
- if not manager.registry:
- raise sa_exc.InvalidRequestError(
- "The _mapper() function may not be invoked directly outside "
- "of a declarative registry."
- " Please use the sqlalchemy.orm.registry.map_imperatively() "
- "function for a classical mapping."
- )
-
self.class_manager = manager
self.registry = manager.registry
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)
+
+ def test_cant_call_legacy_constructor_directly(self):
+ users, User = (
+ self.tables.users,
+ self.classes.User,
+ )
+
+ from sqlalchemy.orm import Mapper
+
+ with expect_raises_message(
+ sa.exc.InvalidRequestError,
+ r"The _mapper\(\) function and Mapper\(\) constructor may not be "
+ "invoked directly",
+ ):
+ Mapper(User, users)
+
def test_prop_shadow(self):
"""A backref name may not shadow an existing property name."""