summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2013-06-30 11:09:37 -0400
committerMike Bayer <mike_mp@zzzcomputing.com>2013-06-30 11:09:37 -0400
commit3b24ccaf28fd85a6e54aa813fde8b3677253f67f (patch)
tree0edd54a7c1a92f95c09415b6f524ccc719a4a7c4 /lib
parent278c243aa35103f07a928222a2b07092796e2099 (diff)
downloadsqlalchemy-3b24ccaf28fd85a6e54aa813fde8b3677253f67f.tar.gz
A warning is emitted when trying to flush an object of an inherited
mapped class where the polymorphic discriminator has been assigned to a value that is invalid for the class. [ticket:2750]
Diffstat (limited to 'lib')
-rw-r--r--lib/sqlalchemy/orm/mapper.py33
-rw-r--r--lib/sqlalchemy/orm/persistence.py3
2 files changed, 35 insertions, 1 deletions
diff --git a/lib/sqlalchemy/orm/mapper.py b/lib/sqlalchemy/orm/mapper.py
index 7f14d83cb..12d2234d2 100644
--- a/lib/sqlalchemy/orm/mapper.py
+++ b/lib/sqlalchemy/orm/mapper.py
@@ -28,7 +28,7 @@ from .interfaces import MapperProperty, _InspectionAttr, _MappedAttribute
from .util import _INSTRUMENTOR, _class_to_mapper, \
_state_mapper, class_mapper, \
- PathRegistry
+ PathRegistry, state_str
import sys
properties = util.importlater("sqlalchemy.orm", "properties")
descriptor_props = util.importlater("sqlalchemy.orm", "descriptor_props")
@@ -1040,6 +1040,8 @@ class Mapper(_InspectionAttr):
if self.polymorphic_on is not None:
self._set_polymorphic_identity = \
mapper._set_polymorphic_identity
+ self._validate_polymorphic_identity = \
+ mapper._validate_polymorphic_identity
else:
self._set_polymorphic_identity = None
return
@@ -1050,10 +1052,39 @@ class Mapper(_InspectionAttr):
state.get_impl(polymorphic_key).set(state, dict_,
state.manager.mapper.polymorphic_identity, None)
+ def _validate_polymorphic_identity(mapper, state, dict_):
+ if dict_[polymorphic_key] not in \
+ mapper._acceptable_polymorphic_identities:
+ util.warn(
+ "Flushing object %s with "
+ "incompatible polymorphic identity %r; the "
+ "object may not refresh and/or load correctly" % (
+ state_str(state),
+ dict_[polymorphic_key]
+ )
+ )
+
self._set_polymorphic_identity = _set_polymorphic_identity
+ self._validate_polymorphic_identity = _validate_polymorphic_identity
else:
self._set_polymorphic_identity = None
+
+ _validate_polymorphic_identity = None
+
+ @_memoized_configured_property
+ def _acceptable_polymorphic_identities(self):
+ identities = set()
+
+ stack = deque([self])
+ while stack:
+ item = stack.popleft()
+ if item.mapped_table is self.mapped_table:
+ identities.add(item.polymorphic_identity)
+ stack.extend(item._inheriting_mappers)
+
+ return identities
+
def _adapt_inherited_property(self, key, prop, init):
if not self.concrete:
self._configure_property(key, prop, init=False, setparent=False)
diff --git a/lib/sqlalchemy/orm/persistence.py b/lib/sqlalchemy/orm/persistence.py
index a773786c4..944623b07 100644
--- a/lib/sqlalchemy/orm/persistence.py
+++ b/lib/sqlalchemy/orm/persistence.py
@@ -150,6 +150,9 @@ def _organize_states_for_save(base_mapper, states, uowtransaction):
else:
mapper.dispatch.before_update(mapper, connection, state)
+ if mapper._validate_polymorphic_identity:
+ mapper._validate_polymorphic_identity(mapper, state, dict_)
+
# detect if we have a "pending" instance (i.e. has
# no instance_key attached to it), and another instance
# with the same identity key already exists as persistent.