summaryrefslogtreecommitdiff
path: root/lib/sqlalchemy/orm/unitofwork.py
diff options
context:
space:
mode:
authorMike Bayer <mike_mp@zzzcomputing.com>2012-07-14 12:52:21 -0400
committerMike Bayer <mike_mp@zzzcomputing.com>2012-07-14 12:52:21 -0400
commit9cab0f1f16a5f5e3b9dff9216911e244ce793544 (patch)
treee3dcb6a704d67885a5cbb8725586dada397ea8e4 /lib/sqlalchemy/orm/unitofwork.py
parent3eaedb38d306095c683900e7e4625be7f7bc91d2 (diff)
downloadsqlalchemy-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.py132
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):