diff options
| author | Mike Bayer <mike_mp@zzzcomputing.com> | 2012-07-14 12:52:21 -0400 |
|---|---|---|
| committer | Mike Bayer <mike_mp@zzzcomputing.com> | 2012-07-14 12:52:21 -0400 |
| commit | 9cab0f1f16a5f5e3b9dff9216911e244ce793544 (patch) | |
| tree | e3dcb6a704d67885a5cbb8725586dada397ea8e4 /lib/sqlalchemy/orm/unitofwork.py | |
| parent | 3eaedb38d306095c683900e7e4625be7f7bc91d2 (diff) | |
| download | sqlalchemy-9cab0f1f16a5f5e3b9dff9216911e244ce793544.tar.gz | |
move mapper-level UOW functionality straight into unitofwork also. there's
no need for this to be per-mapper. can't move dependency init
off of property though as this init needs to happen up front, added a test
to prove it.
Diffstat (limited to 'lib/sqlalchemy/orm/unitofwork.py')
| -rw-r--r-- | lib/sqlalchemy/orm/unitofwork.py | 132 |
1 files changed, 80 insertions, 52 deletions
diff --git a/lib/sqlalchemy/orm/unitofwork.py b/lib/sqlalchemy/orm/unitofwork.py index caf7f2e8e..c0423939f 100644 --- a/lib/sqlalchemy/orm/unitofwork.py +++ b/lib/sqlalchemy/orm/unitofwork.py @@ -14,8 +14,9 @@ organizes them in order of dependency, and executes. from .. import util, event from ..util import topological -from . import attributes, interfaces, persistence, util as orm_util -session = util.importlater("sqlalchemy.orm", "session") +from . import attributes, persistence, util as orm_util + +sessionlib = util.importlater("sqlalchemy.orm", "session") def track_cascade_events(descriptor, prop): """Establish event listeners on object attributes which handle @@ -25,10 +26,10 @@ def track_cascade_events(descriptor, prop): key = prop.key def append(state, item, initiator): - # process "save_update" cascade rules for when + # process "save_update" cascade rules for when # an instance is appended to the list of another instance - sess = session._state_session(state) + sess = sessionlib._state_session(state) if sess: prop = state.manager.mapper._props[key] item_state = attributes.instance_state(item) @@ -39,7 +40,7 @@ def track_cascade_events(descriptor, prop): return item def remove(state, item, initiator): - sess = session._state_session(state) + sess = sessionlib._state_session(state) if sess: prop = state.manager.mapper._props[key] # expunge pending orphans @@ -50,12 +51,12 @@ def track_cascade_events(descriptor, prop): sess.expunge(item) def set_(state, newvalue, oldvalue, initiator): - # process "save_update" cascade rules for when an instance + # process "save_update" cascade rules for when an instance # is attached to another instance if oldvalue is newvalue: return newvalue - sess = session._state_session(state) + sess = sessionlib._state_session(state) if sess: prop = state.manager.mapper._props[key] if newvalue is not None: @@ -85,12 +86,12 @@ class UOWTransaction(object): def __init__(self, session): self.session = session - # dictionary used by external actors to + # dictionary used by external actors to # store arbitrary state information. self.attributes = {} - # dictionary of mappers to sets of - # DependencyProcessors, which are also + # dictionary of mappers to sets of + # DependencyProcessors, which are also # set to be part of the sorted flush actions, # which have that mapper as a parent. self.deps = util.defaultdict(set) @@ -105,7 +106,7 @@ class UOWTransaction(object): # and determine if a flush action is needed self.presort_actions = {} - # dictionary of PostSortRec objects, each + # dictionary of PostSortRec objects, each # one issues work during the flush within # a certain ordering. self.postsort_actions = {} @@ -123,7 +124,7 @@ class UOWTransaction(object): # tracks InstanceStates which will be receiving # a "post update" call. Keys are mappers, - # values are a set of states and a set of the + # 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())) @@ -132,7 +133,7 @@ class UOWTransaction(object): return bool(self.states) def is_deleted(self, state): - """return true if the given state is marked as deleted + """return true if the given state is marked as deleted within this uowtransaction.""" return state in self.states and self.states[state][0] @@ -151,9 +152,10 @@ class UOWTransaction(object): self.states[state] = (isdelete, True) - def get_attribute_history(self, state, key, + def get_attribute_history(self, state, key, passive=attributes.PASSIVE_NO_INITIALIZE): - """facade to attributes.get_state_history(), including caching of results.""" + """facade to attributes.get_state_history(), including + caching of results.""" hashkey = ("history", state, key) @@ -163,13 +165,13 @@ class UOWTransaction(object): if hashkey in self.attributes: history, state_history, cached_passive = self.attributes[hashkey] - # if the cached lookup was "passive" and now + # if the cached lookup was "passive" and now # we want non-passive, do a non-passive lookup and re-cache if not cached_passive & attributes.SQL_OK \ and passive & attributes.SQL_OK: impl = state.manager[key].impl - history = impl.get_history(state, state.dict, + history = impl.get_history(state, state.dict, attributes.PASSIVE_OFF) if history and impl.uses_objects: state_history = history.as_state() @@ -197,13 +199,13 @@ class UOWTransaction(object): if key not in self.presort_actions: self.presort_actions[key] = Preprocess(processor, fromparent) - def register_object(self, state, isdelete=False, + def register_object(self, state, isdelete=False, listonly=False, cancel_delete=False, operation=None, prop=None): if not self.session._contains_state(state): if not state.deleted and operation is not None: util.warn("Object of type %s not in session, %s operation " - "along '%s' will not proceed" % + "along '%s' will not proceed" % (orm_util.state_class_str(state), operation, prop)) return False @@ -211,7 +213,7 @@ class UOWTransaction(object): mapper = state.manager.mapper if mapper not in self.mappers: - mapper._per_mapper_flush_actions(self) + self._per_mapper_flush_actions(mapper) self.mappers[mapper].add(state) self.states[state] = (isdelete, listonly) @@ -226,10 +228,24 @@ class UOWTransaction(object): states.add(state) cols.update(post_update_cols) + def _per_mapper_flush_actions(self, mapper): + saves = SaveUpdateAll(self, mapper.base_mapper) + deletes = DeleteAll(self, mapper.base_mapper) + self.dependencies.add((saves, deletes)) + + for dep in mapper._dependency_processors: + dep.per_property_preprocessors(self) + + for prop in mapper.relationships: + if prop.viewonly: + continue + dep = prop._dependency_processor + dep.per_property_preprocessors(self) + @util.memoized_property def _mapper_for_dep(self): - """return a dynamic mapping of (Mapper, DependencyProcessor) to - True or False, indicating if the DependencyProcessor operates + """return a dynamic mapping of (Mapper, DependencyProcessor) to + True or False, indicating if the DependencyProcessor operates on objects of that Mapper. The result is stored in the dictionary persistently once @@ -237,11 +253,11 @@ class UOWTransaction(object): """ return util.PopulateDict( - lambda tup:tup[0]._props.get(tup[1].key) is tup[1].prop + lambda tup: tup[0]._props.get(tup[1].key) is tup[1].prop ) def filter_states_for_dep(self, dep, states): - """Filter the given list of InstanceStates to those relevant to the + """Filter the given list of InstanceStates to those relevant to the given DependencyProcessor. """ @@ -273,7 +289,7 @@ class UOWTransaction(object): # see if the graph of mapper dependencies has cycles. self.cycles = cycles = topological.find_cycles( - self.dependencies, + self.dependencies, self.postsort_actions.values()) if cycles: @@ -319,20 +335,21 @@ class UOWTransaction(object): # execute if self.cycles: for set_ in topological.sort_as_subsets( - self.dependencies, + self.dependencies, postsort_actions): while set_: n = set_.pop() n.execute_aggregate(self, set_) else: for rec in topological.sort( - self.dependencies, + self.dependencies, postsort_actions): rec.execute(self) def finalize_flush_changes(self): - """mark processed objects as clean / deleted after a successful flush(). + """mark processed objects as clean / deleted after a successful + flush(). this method is called within the flush() method after the execute() method has succeeded and the transaction has been committed. @@ -351,7 +368,8 @@ class IterateMappersMixin(object): def _mappers(self, uow): if self.fromparent: return iter( - m for m in self.dependency_processor.parent.self_and_descendants + m for m in + self.dependency_processor.parent.self_and_descendants if uow._mapper_for_dep[(m, self.dependency_processor)] ) else: @@ -424,7 +442,8 @@ class ProcessAll(IterateMappersMixin, PostSortRec): self.dependency_processor = dependency_processor self.delete = delete self.fromparent = fromparent - uow.deps[dependency_processor.parent.base_mapper].add(dependency_processor) + uow.deps[dependency_processor.parent.base_mapper].\ + add(dependency_processor) def execute(self, uow): states = self._elements(uow) @@ -471,18 +490,23 @@ class SaveUpdateAll(PostSortRec): assert mapper is mapper.base_mapper def execute(self, uow): - persistence.save_obj(self.mapper, + persistence.save_obj(self.mapper, uow.states_for_mapper_hierarchy(self.mapper, False, False), uow ) + def per_state_flush_actions(self, uow): - states = list(uow.states_for_mapper_hierarchy(self.mapper, False, False)) - for rec in self.mapper._per_state_flush_actions( - uow, - states, - False): - yield rec + states = list(uow.states_for_mapper_hierarchy( + self.mapper, False, False)) + base_mapper = self.mapper.base_mapper + delete_all = DeleteAll(uow, base_mapper) + for state in states: + # keep saves before deletes - + # this ensures 'row switch' operations work + action = SaveUpdateState(uow, state, base_mapper) + uow.dependencies.add((action, delete_all)) + yield action for dep in uow.deps[self.mapper]: states_for_prop = uow.filter_states_for_dep(dep, states) @@ -500,12 +524,16 @@ class DeleteAll(PostSortRec): ) def per_state_flush_actions(self, uow): - states = list(uow.states_for_mapper_hierarchy(self.mapper, True, False)) - for rec in self.mapper._per_state_flush_actions( - uow, - states, - True): - yield rec + states = list(uow.states_for_mapper_hierarchy( + self.mapper, True, False)) + base_mapper = self.mapper.base_mapper + save_all = SaveUpdateAll(uow, base_mapper) + for state in states: + # keep saves before deletes - + # this ensures 'row switch' operations work + action = DeleteState(uow, state, base_mapper) + uow.dependencies.add((save_all, action)) + yield action for dep in uow.deps[self.mapper]: states_for_prop = uow.filter_states_for_dep(dep, states) @@ -521,8 +549,8 @@ class ProcessState(PostSortRec): cls_ = self.__class__ dependency_processor = self.dependency_processor delete = self.delete - our_recs = [r for r in recs - if r.__class__ is cls_ and + our_recs = [r for r in recs + if r.__class__ is cls_ and r.dependency_processor is dependency_processor and r.delete is delete] recs.difference_update(our_recs) @@ -548,13 +576,13 @@ class SaveUpdateState(PostSortRec): def execute_aggregate(self, uow, recs): cls_ = self.__class__ mapper = self.mapper - our_recs = [r for r in recs - if r.__class__ is cls_ and + our_recs = [r for r in recs + if r.__class__ is cls_ and r.mapper is mapper] recs.difference_update(our_recs) persistence.save_obj(mapper, - [self.state] + - [r.state for r in our_recs], + [self.state] + + [r.state for r in our_recs], uow) def __repr__(self): @@ -571,13 +599,13 @@ class DeleteState(PostSortRec): def execute_aggregate(self, uow, recs): cls_ = self.__class__ mapper = self.mapper - our_recs = [r for r in recs - if r.__class__ is cls_ and + our_recs = [r for r in recs + if r.__class__ is cls_ and r.mapper is mapper] recs.difference_update(our_recs) states = [self.state] + [r.state for r in our_recs] persistence.delete_obj(mapper, - [s for s in states if uow.states[s][0]], + [s for s in states if uow.states[s][0]], uow) def __repr__(self): |
