From: Mike Bayer Date: Fri, 18 Jun 2010 17:31:51 +0000 (-0400) Subject: this one is actually doing it. removed the cruft we don't need from the old approach. X-Git-Tag: rel_0_6_2~33 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=41cf0a7ac8754fcb6943dee633aeed9d56e460d3;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git this one is actually doing it. removed the cruft we don't need from the old approach. not sure if one-to-many with elaborate self-refs, etc., but we appear to be as good as we were before. --- diff --git a/lib/sqlalchemy/orm/dependency.py b/lib/sqlalchemy/orm/dependency.py index c9a0ce8ed3..f89291ff3f 100644 --- a/lib/sqlalchemy/orm/dependency.py +++ b/lib/sqlalchemy/orm/dependency.py @@ -266,19 +266,13 @@ class DependencyProcessor(object): set ) - def _post_update(self, state, uowcommit, related, processed, immediate): - if processed is not None and state in processed: - return + def _post_update(self, state, uowcommit, related): for x in related: if x is not None: uowcommit.issue_post_update( state, - [r for l, r in self.prop.synchronize_pairs], - immediate + [r for l, r in self.prop.synchronize_pairs] ) - if processed is not None: - processed.add(state) - break def _pks_changed(self, uowcommit, state): @@ -297,12 +291,19 @@ class OneToManyDP(DependencyProcessor): before_delete, ): if self.post_update: + child_post_updates = unitofwork.PostUpdateThing( + uow, self.mapper.primary_base_mapper, False) + child_pre_updates = unitofwork.PostUpdateThing( + uow, self.mapper.primary_base_mapper, True) uow.dependencies.update([ (child_saves, after_save), (parent_saves, after_save), - (before_delete, parent_deletes), - (before_delete, child_deletes), + (after_save, child_post_updates), + + (before_delete, child_pre_updates), + (child_pre_updates, parent_deletes), + (child_pre_updates, child_deletes), ]) else: @@ -326,30 +327,36 @@ class OneToManyDP(DependencyProcessor): isdelete, childisdelete): if self.post_update: + + child_post_updates = unitofwork.PostUpdateThing( + uow, self.mapper.primary_base_mapper, False) + child_pre_updates = unitofwork.PostUpdateThing( + uow, self.mapper.primary_base_mapper, True) # TODO: this whole block is not covered # by any tests if not isdelete: if childisdelete: uow.dependencies.update([ - (save_parent, after_save), - (after_save, child_action), + (child_action, after_save), + (after_save, child_post_updates), ]) else: uow.dependencies.update([ (save_parent, after_save), (child_action, after_save), + (after_save, child_post_updates), ]) else: if childisdelete: uow.dependencies.update([ - (before_delete, delete_parent), - (before_delete, child_action), + (before_delete, child_pre_updates), + (child_pre_updates, delete_parent), ]) else: uow.dependencies.update([ - (before_delete, delete_parent), - (child_action, before_delete), + (before_delete, child_pre_updates), + (child_pre_updates, delete_parent), ]) elif not isdelete: uow.dependencies.update([ @@ -432,8 +439,6 @@ class OneToManyDP(DependencyProcessor): # key to the parent set to NULL this phase can be called # safely for any cascade but is unnecessary if delete cascade # is on. - if self.post_update: - processed = self._get_reversed_processed_set(uowcommit) if self.post_update or not self.passive_deletes == 'all': children_added = uowcommit.memo(('children_added', self), set) @@ -455,7 +460,7 @@ class OneToManyDP(DependencyProcessor): self._post_update( child, uowcommit, - [state], processed, True) + [state]) if self.post_update or not self.cascade.delete: for child in set(history.unchanged).\ difference(children_added): @@ -468,16 +473,13 @@ class OneToManyDP(DependencyProcessor): self._post_update( child, uowcommit, - [state], processed, True) + [state]) # technically, we can even remove each child from the # collection here too. but this would be a somewhat # inconsistent behavior since it wouldn't happen if the old # parent wasn't deleted but child was moved. def process_saves(self, uowcommit, states): - if self.post_update: - processed = self._get_reversed_processed_set(uowcommit) - for state in states: history = uowcommit.get_attribute_history(state, self.key, passive=True) if history: @@ -487,9 +489,7 @@ class OneToManyDP(DependencyProcessor): self._post_update( child, uowcommit, - [state], - processed, - True + [state] ) for child in history.deleted: @@ -537,20 +537,20 @@ class ManyToOneDP(DependencyProcessor): before_delete): if self.post_update: - child_post_updates = unitofwork.PostUpdateThing( + parent_post_updates = unitofwork.PostUpdateThing( uow, self.parent.primary_base_mapper, False) - child_pre_updates = unitofwork.PostUpdateThing( + parent_pre_updates = unitofwork.PostUpdateThing( uow, self.parent.primary_base_mapper, True) uow.dependencies.update([ (child_saves, after_save), (parent_saves, after_save), - (after_save, child_post_updates), + (after_save, parent_post_updates), - (after_save, child_pre_updates), - (before_delete, child_pre_updates), + (after_save, parent_pre_updates), + (before_delete, parent_pre_updates), - (child_pre_updates, child_deletes), + (parent_pre_updates, child_deletes), ]) else: uow.dependencies.update([ @@ -656,8 +656,6 @@ class ManyToOneDP(DependencyProcessor): not self.cascade.delete_orphan and \ not self.passive_deletes == 'all': - processed = self._get_reversed_processed_set(uowcommit) - # post_update means we have to update our # row to not reference the child object # before we can DELETE the row @@ -672,12 +670,9 @@ class ManyToOneDP(DependencyProcessor): self._post_update( state, uowcommit, - history.sum(), processed, False) + history.sum()) def process_saves(self, uowcommit, states): - if self.post_update: - processed = self._get_reversed_processed_set(uowcommit) - for state in states: history = uowcommit.get_attribute_history(state, self.key, passive=True) if history: @@ -687,7 +682,7 @@ class ManyToOneDP(DependencyProcessor): if self.post_update: self._post_update( state, - uowcommit, history.sum(), processed, False) + uowcommit, history.sum()) def _synchronize(self, state, child, associationrow, clearkeys, uowcommit): if state is None or (not self.post_update and uowcommit.is_deleted(state)): diff --git a/lib/sqlalchemy/orm/unitofwork.py b/lib/sqlalchemy/orm/unitofwork.py index aed50f6499..529a94d5dd 100644 --- a/lib/sqlalchemy/orm/unitofwork.py +++ b/lib/sqlalchemy/orm/unitofwork.py @@ -110,7 +110,7 @@ class UOWTransaction(object): # or insert/updated, or just refreshed self.states = {} - self.post_update_states = util.defaultdict(set) + self.post_update_states = util.defaultdict(lambda: (set(), set())) @property def has_work(self): @@ -185,14 +185,11 @@ class UOWTransaction(object): if not listonly and (isdelete or cancel_delete): self.states[state] = (isdelete, False) - def issue_post_update(self, state, post_update_cols, immediate): - if immediate: - mapper = state.manager.mapper.base_mapper - mapper._save_obj([state], self, \ - postupdate=True, \ - post_update_cols=set(post_update_cols)) - else: - self.post_update_states[state].update(post_update_cols) + def issue_post_update(self, state, post_update_cols): + mapper = state.manager.mapper.base_mapper + states, cols = self.post_update_states[mapper] + states.add(state) + cols.update(post_update_cols) @util.memoized_property def _mapper_for_dep(self): @@ -426,18 +423,12 @@ class PostUpdateThing(PostSortRec): self.isdelete = isdelete def execute(self, uow): - states = [] - update_cols = set() - - for state in uow.states_for_mapper_hierarchy(self.mapper, self.isdelete, False): - if state not in uow.post_update_states: - continue - states.append(state) - update_cols.update(uow.post_update_states[state]) - + states, cols = uow.post_update_states[self.mapper] + states = [s for s in states if uow.states[s][0] == self.isdelete] + self.mapper._save_obj(states, uow, \ postupdate=True, \ - post_update_cols=update_cols) + post_update_cols=cols) class SaveUpdateAll(PostSortRec): def __init__(self, uow, mapper): diff --git a/test/orm/test_cycles.py b/test/orm/test_cycles.py index b41a34aa83..e86b2c8ae0 100644 --- a/test/orm/test_cycles.py +++ b/test/orm/test_cycles.py @@ -1018,6 +1018,3 @@ class SelfReferentialPostUpdateTest3(_base.MappedTest): p1.child = None session.flush() - - -