summaryrefslogtreecommitdiff
path: root/lib/sqlalchemy/orm
diff options
context:
space:
mode:
Diffstat (limited to 'lib/sqlalchemy/orm')
-rw-r--r--lib/sqlalchemy/orm/dependency.py18
-rw-r--r--lib/sqlalchemy/orm/mapper.py8
-rw-r--r--lib/sqlalchemy/orm/properties.py14
-rw-r--r--lib/sqlalchemy/orm/session.py18
-rw-r--r--lib/sqlalchemy/orm/unitofwork.py36
5 files changed, 48 insertions, 46 deletions
diff --git a/lib/sqlalchemy/orm/dependency.py b/lib/sqlalchemy/orm/dependency.py
index 39ea1db35..35b197f63 100644
--- a/lib/sqlalchemy/orm/dependency.py
+++ b/lib/sqlalchemy/orm/dependency.py
@@ -152,7 +152,7 @@ class DependencyProcessor(object):
# detect if there's anything changed or loaded
# by a preprocessor on this state/attribute. if not,
# we should be able to skip it entirely.
- sum_ = attributes.get_all_pending(state, state.dict, self.key)
+ sum_ = state.manager[self.key].impl.get_all_pending(state, state.dict)
if not sum_:
continue
@@ -439,10 +439,10 @@ class OneToManyDP(DependencyProcessor):
elif self.hasparent(child) is False:
uowcommit.register_object(child, isdelete=True,
operation="delete", prop=self.prop)
- for c, m in self.mapper.cascade_iterator(
+ for c, m, st_, dct_ in self.mapper.cascade_iterator(
'delete', child):
uowcommit.register_object(
- attributes.instance_state(c),
+ st_,
isdelete=True)
if pks_changed:
@@ -661,10 +661,10 @@ class ManyToOneDP(DependencyProcessor):
continue
uowcommit.register_object(child, isdelete=True,
operation="delete", prop=self.prop)
- for c, m in self.mapper.cascade_iterator(
+ for c, m, st_, dct_ in self.mapper.cascade_iterator(
'delete', child):
uowcommit.register_object(
- attributes.instance_state(c), isdelete=True)
+ st_, isdelete=True)
def presort_saves(self, uowcommit, states):
for state in states:
@@ -681,10 +681,10 @@ class ManyToOneDP(DependencyProcessor):
uowcommit.register_object(child, isdelete=True,
operation="delete", prop=self.prop)
- for c, m in self.mapper.cascade_iterator(
+ for c, m, st_, dct_ in self.mapper.cascade_iterator(
'delete', child):
uowcommit.register_object(
- attributes.instance_state(c),
+ st_,
isdelete=True)
def process_deletes(self, uowcommit, states):
@@ -939,11 +939,11 @@ class ManyToManyDP(DependencyProcessor):
if self.hasparent(child) is False:
uowcommit.register_object(child, isdelete=True,
operation="delete", prop=self.prop)
- for c, m in self.mapper.cascade_iterator(
+ for c, m, st_, dct_ in self.mapper.cascade_iterator(
'delete',
child):
uowcommit.register_object(
- attributes.instance_state(c), isdelete=True)
+ st_, isdelete=True)
def process_deletes(self, uowcommit, states):
secondary_delete = []
diff --git a/lib/sqlalchemy/orm/mapper.py b/lib/sqlalchemy/orm/mapper.py
index 48c37f80d..8bd8bf3c8 100644
--- a/lib/sqlalchemy/orm/mapper.py
+++ b/lib/sqlalchemy/orm/mapper.py
@@ -1382,10 +1382,10 @@ class Mapper(object):
reference so that they don't fall out of scope immediately.
"""
- visited_instances = util.IdentitySet()
+ visited_states = set()
prp, mpp = object(), object()
- visitables = [(deque(self._props.values()), prp, state, state.dict)]
+ visitables = deque([(deque(self._props.values()), prp, state, state.dict)])
while visitables:
iterator, item_type, parent_state, parent_dict = visitables[-1]
@@ -1398,13 +1398,13 @@ class Mapper(object):
if type_ not in prop.cascade:
continue
queue = deque(prop.cascade_iterator(type_, parent_state,
- parent_dict, visited_instances, halt_on))
+ parent_dict, visited_states, halt_on))
if queue:
visitables.append((queue,mpp, None, None))
elif item_type is mpp:
instance, instance_mapper, corresponding_state, \
corresponding_dict = iterator.popleft()
- yield (instance, instance_mapper)
+ yield instance, instance_mapper, corresponding_state, corresponding_dict
visitables.append((deque(instance_mapper._props.values()),
prp, corresponding_state, corresponding_dict))
diff --git a/lib/sqlalchemy/orm/properties.py b/lib/sqlalchemy/orm/properties.py
index 239159f3e..1b144ea74 100644
--- a/lib/sqlalchemy/orm/properties.py
+++ b/lib/sqlalchemy/orm/properties.py
@@ -820,7 +820,7 @@ class RelationshipProperty(StrategizedProperty):
dest_state.get_impl(self.key).set(dest_state,
dest_dict, obj, None)
- def cascade_iterator(self, type_, state, dict_, visited_instances, halt_on=None):
+ def cascade_iterator(self, type_, state, dict_, visited_states, halt_on=None):
if not type_ in self.cascade:
return
@@ -831,7 +831,7 @@ class RelationshipProperty(StrategizedProperty):
passive = attributes.PASSIVE_OFF
if type_ == 'save-update':
- instances = attributes.get_all_pending(state, dict_, self.key)
+ instances = state.manager[self.key].impl.get_all_pending(state, dict_)
else:
instances = state.value_as_iterable(dict_, self.key,
@@ -842,10 +842,12 @@ class RelationshipProperty(StrategizedProperty):
if instances:
for c in instances:
if c is not None and \
- c is not attributes.PASSIVE_NO_RESULT and \
- c not in visited_instances:
-
+ c is not attributes.PASSIVE_NO_RESULT:
+
instance_state = attributes.instance_state(c)
+ if instance_state in visited_states:
+ continue
+
instance_dict = attributes.instance_dict(c)
if halt_on and halt_on(instance_state):
@@ -865,7 +867,7 @@ class RelationshipProperty(StrategizedProperty):
c.__class__
))
- visited_instances.add(c)
+ visited_states.add(instance_state)
# cascade using the mapper local to this
# object, so that its individual properties are located
diff --git a/lib/sqlalchemy/orm/session.py b/lib/sqlalchemy/orm/session.py
index e2c1308b8..ba1a38bb9 100644
--- a/lib/sqlalchemy/orm/session.py
+++ b/lib/sqlalchemy/orm/session.py
@@ -1105,13 +1105,10 @@ class Session(object):
def _save_or_update_state(self, state):
self._save_or_update_impl(state)
- self._cascade_save_or_update(state)
- def _cascade_save_or_update(self, state):
- for state, mapper in _cascade_unknown_state_iterator(
- 'save-update', state,
- halt_on=self._contains_state):
- self._save_or_update_impl(state)
+ mapper = _state_mapper(state)
+ for o, m, st_, dct_ in mapper.cascade_iterator('save-update', state, halt_on=self._contains_state):
+ self._save_or_update_impl(st_)
def delete(self, instance):
"""Mark an instance as deleted.
@@ -1603,13 +1600,8 @@ def _cascade_state_iterator(cascade, state, **kwargs):
# yield the state, object, mapper. yielding the object
# allows the iterator's results to be held in a list without
# states being garbage collected
- for (o, m) in mapper.cascade_iterator(cascade, state, **kwargs):
- yield attributes.instance_state(o), o, m
-
-def _cascade_unknown_state_iterator(cascade, state, **kwargs):
- mapper = _state_mapper(state)
- for (o, m) in mapper.cascade_iterator(cascade, state, **kwargs):
- yield _state_for_unknown_persistence_instance(o), m
+ for o, m, st_, dct_ in mapper.cascade_iterator(cascade, state, **kwargs):
+ yield st_, o, m
def _state_for_unsaved_instance(instance, create=False):
try:
diff --git a/lib/sqlalchemy/orm/unitofwork.py b/lib/sqlalchemy/orm/unitofwork.py
index 1e1eda4a3..17b73e5f9 100644
--- a/lib/sqlalchemy/orm/unitofwork.py
+++ b/lib/sqlalchemy/orm/unitofwork.py
@@ -28,7 +28,9 @@ class UOWEventHandler(interfaces.AttributeExtension):
def __init__(self, key):
self.key = key
-
+
+ # TODO: migrate these to unwrapped events
+
def append(self, state, item, initiator):
# process "save_update" cascade rules for when
# an instance is appended to the list of another instance
@@ -36,10 +38,11 @@ class UOWEventHandler(interfaces.AttributeExtension):
sess = session._state_session(state)
if sess:
prop = _state_mapper(state)._props[self.key]
+ item_state = attributes.instance_state(item)
if prop.cascade.save_update and \
(prop.cascade_backrefs or self.key == initiator.key) and \
- item not in sess:
- sess.add(item)
+ not sess._contains_state(item_state):
+ sess._save_or_update_state(item_state)
return item
def remove(self, state, item, initiator):
@@ -47,9 +50,10 @@ class UOWEventHandler(interfaces.AttributeExtension):
if sess:
prop = _state_mapper(state)._props[self.key]
# expunge pending orphans
+ item_state = attributes.instance_state(item)
if prop.cascade.delete_orphan and \
- item in sess.new and \
- prop.mapper._is_orphan(attributes.instance_state(item)):
+ item_state in sess._new and \
+ prop.mapper._is_orphan(item_state):
sess.expunge(item)
def set(self, state, newvalue, oldvalue, initiator):
@@ -61,15 +65,19 @@ class UOWEventHandler(interfaces.AttributeExtension):
sess = session._state_session(state)
if sess:
prop = _state_mapper(state)._props[self.key]
- if newvalue is not None and \
- prop.cascade.save_update and \
- (prop.cascade_backrefs or self.key == initiator.key) and \
- newvalue not in sess:
- sess.add(newvalue)
- if prop.cascade.delete_orphan and \
- oldvalue in sess.new and \
- prop.mapper._is_orphan(attributes.instance_state(oldvalue)):
- sess.expunge(oldvalue)
+ if newvalue is not None:
+ newvalue_state = attributes.instance_state(newvalue)
+ if prop.cascade.save_update and \
+ (prop.cascade_backrefs or self.key == initiator.key) and \
+ not sess._contains_state(newvalue_state):
+ sess._save_or_update_state(newvalue_state)
+
+ if oldvalue is not None and prop.cascade.delete_orphan:
+ oldvalue_state = attributes.instance_state(oldvalue)
+
+ if oldvalue_state in sess._new and \
+ prop.mapper._is_orphan(oldvalue_state):
+ sess.expunge(oldvalue)
return newvalue