From: Mike Bayer Date: Tue, 6 Apr 2010 01:29:51 +0000 (-0400) Subject: beginning to get post_update working, will need more tests X-Git-Tag: rel_0_6_0~52 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=c01d35f15db4e2caf6ce9b48721ecfa232c67813;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git beginning to get post_update working, will need more tests --- diff --git a/lib/sqlalchemy/orm/dependency.py b/lib/sqlalchemy/orm/dependency.py index 662b8c5712..63a34cd0ab 100644 --- a/lib/sqlalchemy/orm/dependency.py +++ b/lib/sqlalchemy/orm/dependency.py @@ -35,10 +35,10 @@ class DependencyProcessor(object): self.passive_updates = prop.passive_updates self.enable_typechecks = prop.enable_typechecks self.key = prop.key - #self.dependency_marker = MapperStub(self.parent, self.mapper, self.key) if not self.prop.synchronize_pairs: raise sa_exc.ArgumentError("Can't build a DependencyProcessor for relationship %s. " - "No target attributes to populate between parent and child are present" % self.prop) + "No target attributes to populate between parent and child are present" % + self.prop) def _get_instrumented_attribute(self): """Return the ``InstrumentedAttribute`` handled by this @@ -215,6 +215,15 @@ class DependencyProcessor(object): """ raise NotImplementedError() + def _check_reverse(self, uow): + for p in self.prop._reverse_property: + if not p.viewonly and p._dependency_processor and \ + (unitofwork.ProcessAll, p._dependency_processor, False, True) in \ + uow.postsort_actions: + return True + else: + return False + def _check_reverse_action(self, uowcommit, parent, child, action): """Determine if an action has been performed by the 'reverse' property of this property. @@ -253,8 +262,9 @@ class DependencyProcessor(object): if state is not None and self.post_update: for x in related: if x is not None and not self._check_reverse_action(uowcommit, x, state, "postupdate"): - uowcommit.register_object(state, postupdate=True, - post_update_cols=[r for l, r in self.prop.synchronize_pairs]) + uowcommit.issue_post_update(state, [r for l, r in self.prop.synchronize_pairs]) + #uowcommit.register_object(state, postupdate=True, + # post_update_cols=[r for l, r in self.prop.synchronize_pairs]) self._performed_action(uowcommit, x, state, "postupdate") break @@ -273,6 +283,7 @@ class OneToManyDP(DependencyProcessor): after_save, before_delete): if self.post_update: + # TODO: childisdelete logic ? uow.dependencies.update([ (child_saves, after_save), (parent_saves, after_save), @@ -298,7 +309,19 @@ class OneToManyDP(DependencyProcessor): child_action, after_save, before_delete, isdelete, childisdelete): - if not isdelete: + + if self.post_update: + if not isdelete: + uow.dependencies.update([ + (save_parent, after_save), + (child_action, after_save) + ]) + else: + uow.dependencies.update([ + (before_delete, delete_parent), + (before_delete, child_action) + ]) + elif not isdelete: uow.dependencies.update([ (save_parent, after_save), (after_save, child_action), @@ -440,11 +463,37 @@ class ManyToOneDP(DependencyProcessor): child_action, after_save, before_delete, isdelete, childisdelete): - if not isdelete: - uow.dependencies.update([ - (child_action, after_save), - (after_save, save_parent), - ]) + + if self.post_update: + if not isdelete: + if childisdelete: + uow.dependencies.update([ + (save_parent, after_save), + (after_save, child_action) + ]) + else: + uow.dependencies.update([ + (save_parent, after_save), + (child_action, after_save) + ]) + else: + # TODO: childisdelete logic here? + uow.dependencies.update([ + (before_delete, delete_parent), + (before_delete, child_action) + ]) + + elif not isdelete: + if not childisdelete: + uow.dependencies.update([ + (child_action, after_save), + (after_save, save_parent), + ]) + else: + uow.dependencies.update([ + (after_save, save_parent), + ]) + else: if childisdelete: uow.dependencies.update([ @@ -559,6 +608,9 @@ class DetectKeySwitch(DependencyProcessor): (parent_saves, after_save) ]) + def per_state_flush_actions(self, uow, states, isdelete): + pass + def presort_deletes(self, uowcommit, states): assert False @@ -605,7 +657,19 @@ class DetectKeySwitch(DependencyProcessor): class ManyToManyDP(DependencyProcessor): - + + def per_property_flush_actions(self, uow): + if self._check_reverse(uow): + return + else: + DependencyProcessor.per_property_flush_actions(self, uow) + + def per_state_flush_actions(self, uow, states, isdelete): + if self._check_reverse(uow): + return + else: + DependencyProcessor.per_state_flush_actions(self, uow, states, isdelete) + def per_property_dependencies(self, uow, parent_saves, child_saves, parent_deletes, @@ -664,13 +728,11 @@ class ManyToManyDP(DependencyProcessor): passive=self.passive_deletes) if history: for child in history.non_added(): - if child is None or \ - self._check_reverse_action(uowcommit, child, state, "manytomany"): + if child is None: continue associationrow = {} self._synchronize(state, child, associationrow, False, uowcommit) secondary_delete.append(associationrow) - self._performed_action(uowcommit, state, child, "manytomany") self._run_crud(uowcommit, secondary_insert, secondary_update, secondary_delete) @@ -683,19 +745,16 @@ class ManyToManyDP(DependencyProcessor): history = uowcommit.get_attribute_history(state, self.key) if history: for child in history.added: - if child is None or \ - self._check_reverse_action(uowcommit, child, state, "manytomany"): + if child is None: continue associationrow = {} self._synchronize(state, child, associationrow, False, uowcommit) - self._performed_action(uowcommit, state, child, "manytomany") secondary_insert.append(associationrow) for child in history.deleted: - if child is None or self._check_reverse_action(uowcommit, child, state, "manytomany"): + if child is None: continue associationrow = {} self._synchronize(state, child, associationrow, False, uowcommit) - self._performed_action(uowcommit, state, child, "manytomany") secondary_delete.append(associationrow) if not self.passive_updates and self._pks_changed(uowcommit, state): diff --git a/lib/sqlalchemy/orm/unitofwork.py b/lib/sqlalchemy/orm/unitofwork.py index 16a01bdc1d..0e613a95fe 100644 --- a/lib/sqlalchemy/orm/unitofwork.py +++ b/lib/sqlalchemy/orm/unitofwork.py @@ -150,6 +150,12 @@ class UOWTransaction(object): if isdelete and not existing_isdelete: raise Exception("Can't upgrade from a save to a delete") + def issue_post_update(self, state, post_update_cols): + mapper = state.manager.mapper.base_mapper + mapper._save_obj([state], self, \ + postupdate=True, \ + post_update_cols=set(post_update_cols)) + def states_for_mapper(self, mapper, isdelete, listonly): checktup = (isdelete, listonly) for state in self.mappers[mapper]: diff --git a/test/orm/test_cycles.py b/test/orm/test_cycles.py index a785840b21..002095b8c7 100644 --- a/test/orm/test_cycles.py +++ b/test/orm/test_cycles.py @@ -801,12 +801,14 @@ class SelfReferentialPostUpdateTest(_base.MappedTest): session.add(root) session.flush() + print "-------------------" remove_child(root, cats) # pre-trigger lazy loader on 'cats' to make the test easier cats.children self.assert_sql_execution( testing.db, session.flush, + AllOf( CompiledSQL("UPDATE node SET prev_sibling_id=:prev_sibling_id " "WHERE node.id = :node_id", lambda ctx:{'prev_sibling_id':about.id, 'node_id':stories.id}), @@ -818,6 +820,7 @@ class SelfReferentialPostUpdateTest(_base.MappedTest): CompiledSQL("UPDATE node SET next_sibling_id=:next_sibling_id " "WHERE node.id = :node_id", lambda ctx:{'next_sibling_id':None, 'node_id':cats.id}), + ), CompiledSQL("DELETE FROM node WHERE node.id = :id", lambda ctx:[{'id':cats.id}])