From: Mike Bayer Date: Fri, 18 Jun 2010 19:42:01 +0000 (-0400) Subject: - cleanup of new post update, changelog X-Git-Tag: rel_0_6_2~31 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=f272783f2770e7a0d38c4788e1bac5c0258f5069;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git - cleanup of new post update, changelog --- diff --git a/CHANGES b/CHANGES index b2dd60b54c..15c3a43599 100644 --- a/CHANGES +++ b/CHANGES @@ -11,6 +11,15 @@ CHANGES two objects made to mutually reference each other in one flush would fail to insert a row for both sides. Regression from 0.5. [ticket:1824] + + - the post_update feature of relationship() has been + reworked architecturally to integrate more closely + with the new 0.6 unit of work. The motivation + for the change is so that multiple "post update" + calls, each affecting different foreign key + columns of the same row, are executed in a single + UPDATE statement, rather than one UPDATE + statement per column per row. - Query.statement, Query.subquery(), etc. now transfer the values of bind parameters, i.e. those specified diff --git a/lib/sqlalchemy/orm/dependency.py b/lib/sqlalchemy/orm/dependency.py index f89291ff3f..1616561ae6 100644 --- a/lib/sqlalchemy/orm/dependency.py +++ b/lib/sqlalchemy/orm/dependency.py @@ -291,10 +291,14 @@ 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) + child_post_updates = unitofwork.IssuePostUpdate( + uow, + self.mapper.primary_base_mapper, + False) + child_pre_updates = unitofwork.IssuePostUpdate( + uow, + self.mapper.primary_base_mapper, + True) uow.dependencies.update([ (child_saves, after_save), @@ -328,10 +332,14 @@ class OneToManyDP(DependencyProcessor): 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) + child_post_updates = unitofwork.IssuePostUpdate( + uow, + self.mapper.primary_base_mapper, + False) + child_pre_updates = unitofwork.IssuePostUpdate( + uow, + self.mapper.primary_base_mapper, + True) # TODO: this whole block is not covered # by any tests @@ -457,10 +465,8 @@ class OneToManyDP(DependencyProcessor): child, None, True, uowcommit) if self.post_update and child: - self._post_update( - child, - uowcommit, - [state]) + self._post_update(child, uowcommit, [state]) + if self.post_update or not self.cascade.delete: for child in set(history.unchanged).\ difference(children_added): @@ -470,10 +476,8 @@ class OneToManyDP(DependencyProcessor): child, None, True, uowcommit) if self.post_update and child: - self._post_update( - child, - uowcommit, - [state]) + self._post_update(child, uowcommit, [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 @@ -486,11 +490,7 @@ class OneToManyDP(DependencyProcessor): for child in history.added: self._synchronize(state, child, None, False, uowcommit) if child is not None and self.post_update: - self._post_update( - child, - uowcommit, - [state] - ) + self._post_update(child, uowcommit, [state]) for child in history.deleted: if not self.cascade.delete_orphan and \ @@ -537,10 +537,14 @@ class ManyToOneDP(DependencyProcessor): before_delete): if self.post_update: - parent_post_updates = unitofwork.PostUpdateThing( - uow, self.parent.primary_base_mapper, False) - parent_pre_updates = unitofwork.PostUpdateThing( - uow, self.parent.primary_base_mapper, True) + parent_post_updates = unitofwork.IssuePostUpdate( + uow, + self.parent.primary_base_mapper, + False) + parent_pre_updates = unitofwork.IssuePostUpdate( + uow, + self.parent.primary_base_mapper, + True) uow.dependencies.update([ (child_saves, after_save), @@ -570,8 +574,10 @@ class ManyToOneDP(DependencyProcessor): if self.post_update: if not isdelete: - parent_post_updates = unitofwork.PostUpdateThing( - uow, self.parent.primary_base_mapper, False) + parent_post_updates = unitofwork.IssuePostUpdate( + uow, + self.parent.primary_base_mapper, + False) if childisdelete: uow.dependencies.update([ (after_save, parent_post_updates), @@ -585,8 +591,10 @@ class ManyToOneDP(DependencyProcessor): (after_save, parent_post_updates) ]) else: - parent_pre_updates = unitofwork.PostUpdateThing( - uow, self.parent.primary_base_mapper, True) + parent_pre_updates = unitofwork.IssuePostUpdate( + uow, + self.parent.primary_base_mapper, + True) uow.dependencies.update([ (before_delete, parent_pre_updates), @@ -667,10 +675,7 @@ class ManyToOneDP(DependencyProcessor): self.key, passive=self.passive_deletes) if history: - self._post_update( - state, - uowcommit, - history.sum()) + self._post_update(state, uowcommit, history.sum()) def process_saves(self, uowcommit, states): for state in states: @@ -680,9 +685,7 @@ class ManyToOneDP(DependencyProcessor): self._synchronize(state, child, None, False, uowcommit) if self.post_update: - self._post_update( - state, - uowcommit, history.sum()) + self._post_update(state, 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 7dc5d69b9f..2a6f439bca 100644 --- a/lib/sqlalchemy/orm/unitofwork.py +++ b/lib/sqlalchemy/orm/unitofwork.py @@ -110,6 +110,10 @@ class UOWTransaction(object): # or insert/updated, or just refreshed self.states = {} + # tracks InstanceStates which will be receiving + # a "post update" call. Keys are mappers, + # values are a set of states and a set of the + # columns which should be included in the update. self.post_update_states = util.defaultdict(lambda: (set(), set())) @property @@ -417,7 +421,7 @@ class ProcessAll(IterateMappersMixin, PostSortRec): if isdelete == self.delete and not listonly: yield state -class PostUpdateThing(PostSortRec): +class IssuePostUpdate(PostSortRec): def __init__(self, uow, mapper, isdelete): self.mapper = mapper self.isdelete = isdelete diff --git a/test/orm/test_cycles.py b/test/orm/test_cycles.py index 8848831217..896f7f07d5 100644 --- a/test/orm/test_cycles.py +++ b/test/orm/test_cycles.py @@ -925,7 +925,7 @@ class SelfReferentialPostUpdateTest2(_base.MappedTest): pass @testing.resolve_artifact_names - def test_basic(self): + def test_one(self): """ Test that post_update remembers to be involved in update operations as well, since it replaces the normal dependency processing completely