summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--lib/sqlalchemy/orm/attributes.py75
-rw-r--r--lib/sqlalchemy/orm/dependency.py5
-rw-r--r--lib/sqlalchemy/orm/dynamic.py8
-rw-r--r--lib/sqlalchemy/orm/properties.py67
-rw-r--r--lib/sqlalchemy/orm/state.py21
5 files changed, 86 insertions, 90 deletions
diff --git a/lib/sqlalchemy/orm/attributes.py b/lib/sqlalchemy/orm/attributes.py
index e7ab4c3a1..5c2ae28e2 100644
--- a/lib/sqlalchemy/orm/attributes.py
+++ b/lib/sqlalchemy/orm/attributes.py
@@ -317,6 +317,21 @@ class AttributeImpl(object):
raise NotImplementedError()
def get_all_pending(self, state, dict_):
+ """Return a list of tuples of (state, obj)
+ for all objects in this attribute's current state
+ + history.
+
+ Only applies to object-based attributes.
+
+ This is an inlining of existing functionality
+ which roughly correponds to:
+
+ get_state_history(
+ state,
+ key,
+ passive=PASSIVE_NO_INITIALIZE).sum()
+
+ """
raise NotImplementedError()
def initialize(self, state, dict_):
@@ -547,13 +562,20 @@ class ScalarObjectAttributeImpl(ScalarAttributeImpl):
if self.key in state.committed_state:
original = state.committed_state[self.key]
- if original not in (NEVER_SET, None) and \
+ if original not in (NEVER_SET, PASSIVE_NO_RESULT, None) and \
original is not current:
- return [current, original]
- return [current]
- else:
- return []
+ if current is not None:
+ return [
+ (instance_state(current), current),
+ (instance_state(original), original)
+ ]
+ else:
+ return [(instance_state(original), original)]
+
+ if current is not None:
+ return [(instance_state(current), current)]
+ return []
def set(self, state, dict_, value, initiator, passive=PASSIVE_OFF):
"""Set a value on the given InstanceState.
@@ -645,9 +667,6 @@ class CollectionAttributeImpl(AttributeImpl):
return History.from_collection(self, state, current)
def get_all_pending(self, state, dict_):
- # this is basically an inline
- # of self.get_history().sum()
-
if self.key not in dict_:
return []
else:
@@ -656,22 +675,23 @@ class CollectionAttributeImpl(AttributeImpl):
current = self.get_collection(state, dict_, current)
if self.key not in state.committed_state:
- return list(current)
+ return [(instance_state(o), o) for o in current]
original = state.committed_state[self.key]
if original is NO_VALUE:
- return list(current)
+ return [(instance_state(o), o) for o in current]
else:
- # TODO: use the dict() of state, obj here
- current_set = util.IdentitySet(current)
- original_set = util.IdentitySet(original)
-
- # ensure ordering is maintained
+ current_states = [(instance_state(c), c) for c in current]
+ original_states = [(instance_state(c), c) for c in original]
+
+ current_set = dict(current_states)
+ original_set = dict(original_states)
+
return \
- [x for x in current if x not in original_set] + \
- [x for x in current if x in original_set] + \
- [x for x in original if x not in current_set]
+ [(s, o) for s, o in current_states if s not in original_set] + \
+ [(s, o) for s, o in current_states if s in original_set] + \
+ [(s, o) for s, o in original_states if s not in current_set]
def fire_append_event(self, state, dict_, value, initiator):
for fn in self.dispatch.on_append:
@@ -1072,25 +1092,6 @@ def get_history(obj, key, **kwargs):
def get_state_history(state, key, **kwargs):
return state.get_history(key, **kwargs)
-def get_all_pending(state, dict_, key):
- """Return a list of all objects currently in memory
- involving the given key on the given state.
-
- This should be equivalent to::
-
- get_state_history(
- state,
- key,
- passive=PASSIVE_NO_INITIALIZE).sum()
-
- TODO: we'd like to more closely merge the "history" tuple
- generation with "get_all_pending()", making the presence
- of the "History" object optional.
-
- """
-
- return state.manager[key].impl.get_all_pending(state, dict_)
-
def has_parent(cls, obj, key, optimistic=False):
"""TODO"""
diff --git a/lib/sqlalchemy/orm/dependency.py b/lib/sqlalchemy/orm/dependency.py
index 35b197f63..2c4cc3eff 100644
--- a/lib/sqlalchemy/orm/dependency.py
+++ b/lib/sqlalchemy/orm/dependency.py
@@ -175,10 +175,7 @@ class DependencyProcessor(object):
if child_in_cycles:
child_actions = []
- for child in sum_:
- if child is None:
- continue
- child_state = attributes.instance_state(child)
+ for child_state, child in sum_:
if child_state not in uow.states:
child_action = (None, None)
else:
diff --git a/lib/sqlalchemy/orm/dynamic.py b/lib/sqlalchemy/orm/dynamic.py
index 4637bad7e..2094b1cb0 100644
--- a/lib/sqlalchemy/orm/dynamic.py
+++ b/lib/sqlalchemy/orm/dynamic.py
@@ -144,9 +144,11 @@ class DynamicAttributeImpl(attributes.AttributeImpl):
def get_all_pending(self, state, dict_):
c = self._get_collection_history(state, True)
- return (c.added_items or []) +\
- (c.unchanged_items or []) +\
- (c.deleted_items or [])
+ return [
+ (attributes.instance_state(x), x)
+ for x in
+ c.added_items + c.unchanged_items + c.deleted_items
+ ]
def _get_collection_history(self, state, passive=False):
if self.key in state.committed_state:
diff --git a/lib/sqlalchemy/orm/properties.py b/lib/sqlalchemy/orm/properties.py
index da6d309e0..d1b725c8e 100644
--- a/lib/sqlalchemy/orm/properties.py
+++ b/lib/sqlalchemy/orm/properties.py
@@ -830,47 +830,42 @@ class RelationshipProperty(StrategizedProperty):
passive = attributes.PASSIVE_OFF
if type_ == 'save-update':
- instances = state.manager[self.key].impl.get_all_pending(state, dict_)
+ tuples = state.manager[self.key].impl.\
+ get_all_pending(state, dict_)
else:
- instances = state.value_as_iterable(dict_, self.key,
- passive=passive)
+ tuples = state.value_as_iterable(dict_, self.key,
+ passive=passive)
+
skip_pending = type_ == 'refresh-expire' and 'delete-orphan' \
not in self.cascade
- if instances:
- for c in instances:
- if c is not None and \
- c is not attributes.PASSIVE_NO_RESULT:
-
- instance_state = attributes.instance_state(c)
- if instance_state in visited_states:
- continue
-
- instance_dict = attributes.instance_dict(c)
-
- if halt_on and halt_on(instance_state):
- continue
-
- if skip_pending and not instance_state.key:
- continue
-
- instance_mapper = instance_state.manager.mapper
-
- if not instance_mapper.isa(self.mapper.class_manager.mapper):
- raise AssertionError("Attribute '%s' on class '%s' "
- "doesn't handle objects "
- "of type '%s'" % (
- self.key,
- self.parent.class_,
- c.__class__
- ))
-
- visited_states.add(instance_state)
-
- # cascade using the mapper local to this
- # object, so that its individual properties are located
- yield c, instance_mapper, instance_state, instance_dict
+ for instance_state, c in tuples:
+ if instance_state in visited_states:
+ continue
+
+ instance_dict = attributes.instance_dict(c)
+
+ if halt_on and halt_on(instance_state):
+ continue
+
+ if skip_pending and not instance_state.key:
+ continue
+
+ instance_mapper = instance_state.manager.mapper
+
+ if not instance_mapper.isa(self.mapper.class_manager.mapper):
+ raise AssertionError("Attribute '%s' on class '%s' "
+ "doesn't handle objects "
+ "of type '%s'" % (
+ self.key,
+ self.parent.class_,
+ c.__class__
+ ))
+
+ visited_states.add(instance_state)
+
+ yield c, instance_mapper, instance_state, instance_dict
def _add_reverse_property(self, key):
diff --git a/lib/sqlalchemy/orm/state.py b/lib/sqlalchemy/orm/state.py
index 392d83d94..fd55cfcb8 100644
--- a/lib/sqlalchemy/orm/state.py
+++ b/lib/sqlalchemy/orm/state.py
@@ -128,22 +128,23 @@ class InstanceState(object):
return self.pending[key]
def value_as_iterable(self, dict_, key, passive=PASSIVE_OFF):
- """return an InstanceState attribute as a list,
- regardless of it being a scalar or collection-based
- attribute.
+ """Return a list of tuples (state, obj) for the given
+ key.
- returns None if passive is not PASSIVE_OFF and the getter returns
- PASSIVE_NO_RESULT.
+ returns an empty list if the value is None/empty/PASSIVE_NO_RESULT
"""
- impl = self.get_impl(key)
+ impl = self.manager[key].impl
x = impl.get(self, dict_, passive=passive)
- if x is PASSIVE_NO_RESULT:
- return None
+ if x is PASSIVE_NO_RESULT or x is None:
+ return []
elif hasattr(impl, 'get_collection'):
- return impl.get_collection(self, dict_, x, passive=passive)
+ return [
+ (attributes.instance_state(o), o) for o in
+ impl.get_collection(self, dict_, x, passive=passive)
+ ]
else:
- return [x]
+ return [(attributes.instance_state(x), x)]
def __getstate__(self):
d = {'instance':self.obj()}