diff options
| author | Mike Bayer <mike_mp@zzzcomputing.com> | 2008-10-22 16:09:19 +0000 |
|---|---|---|
| committer | Mike Bayer <mike_mp@zzzcomputing.com> | 2008-10-22 16:09:19 +0000 |
| commit | c35621969027ae052bdcff98a6c7d30e98e54a0e (patch) | |
| tree | 0ba4e0dd5ef12cbe6cc9cdca2a6ffc4d6ab2e8a4 /lib/sqlalchemy | |
| parent | ee7e964ad46d8e1560689e7fdfd3f119d195bc04 (diff) | |
| download | sqlalchemy-c35621969027ae052bdcff98a6c7d30e98e54a0e.tar.gz | |
- Added more granularity to internal attribute access, such
that cascade and flush operations will not initialize
unloaded attributes and collections, leaving them intact for
a lazy-load later on. Backref events still initialize
attrbutes and collections for pending instances.
[ticket:1202]
Diffstat (limited to 'lib/sqlalchemy')
| -rw-r--r-- | lib/sqlalchemy/orm/attributes.py | 66 | ||||
| -rw-r--r-- | lib/sqlalchemy/orm/properties.py | 7 |
2 files changed, 48 insertions, 25 deletions
diff --git a/lib/sqlalchemy/orm/attributes.py b/lib/sqlalchemy/orm/attributes.py index 0424d9b7c..4429d44f4 100644 --- a/lib/sqlalchemy/orm/attributes.py +++ b/lib/sqlalchemy/orm/attributes.py @@ -34,6 +34,21 @@ ATTR_WAS_SET = util.symbol('ATTR_WAS_SET') NO_VALUE = util.symbol('NO_VALUE') NEVER_SET = util.symbol('NEVER_SET') +# "passive" get settings +# TODO: the True/False values need to be factored out +# of the rest of ORM code +# don't fire off any callables, and don't initialize the attribute to +# an empty value +PASSIVE_NO_INITIALIZE = True #util.symbol('PASSIVE_NO_INITIALIZE') + +# don't fire off any callables, but if no callables present +# then initialize to an empty value/collection +# this is used by backrefs. +PASSIVE_NO_CALLABLES = util.symbol('PASSIVE_NO_CALLABLES') + +# fire callables/initialize as needed +PASSIVE_OFF = False #util.symbol('PASSIVE_OFF') + INSTRUMENTATION_MANAGER = '__sa_instrumentation_manager__' """Attribute, elects custom instrumentation when present on a mapped class. @@ -290,7 +305,7 @@ class AttributeImpl(object): else: state.callables[self.key] = callable_ - def get_history(self, state, passive=False): + def get_history(self, state, passive=PASSIVE_OFF): raise NotImplementedError() def _get_callable(self, state): @@ -307,7 +322,7 @@ class AttributeImpl(object): state.dict[self.key] = None return None - def get(self, state, passive=False): + def get(self, state, passive=PASSIVE_OFF): """Retrieve a value from the given object. If a callable is assembled on this object's attribute, and @@ -320,9 +335,12 @@ class AttributeImpl(object): except KeyError: # if no history, check for lazy callables, etc. if state.committed_state.get(self.key, NEVER_SET) is NEVER_SET: + if passive is PASSIVE_NO_INITIALIZE: + return PASSIVE_NORESULT + callable_ = self._get_callable(state) if callable_ is not None: - if passive: + if passive is not PASSIVE_OFF: return PASSIVE_NORESULT value = callable_() if value is not ATTR_WAS_SET: @@ -335,16 +353,16 @@ class AttributeImpl(object): # Return a new, empty value return self.initialize(state) - def append(self, state, value, initiator, passive=False): + def append(self, state, value, initiator, passive=PASSIVE_OFF): self.set(state, value, initiator) - def remove(self, state, value, initiator, passive=False): + def remove(self, state, value, initiator, passive=PASSIVE_OFF): self.set(state, None, initiator) def set(self, state, value, initiator): raise NotImplementedError() - def get_committed_value(self, state, passive=False): + def get_committed_value(self, state, passive=PASSIVE_OFF): """return the unchanged value of this attribute""" if self.key in state.committed_state: @@ -387,7 +405,7 @@ class ScalarAttributeImpl(AttributeImpl): else: del state.dict[self.key] - def get_history(self, state, passive=False): + def get_history(self, state, passive=PASSIVE_OFF): return History.from_attribute( self, state, state.dict.get(self.key, NO_VALUE)) @@ -439,7 +457,7 @@ class MutableScalarAttributeImpl(ScalarAttributeImpl): raise sa_exc.ArgumentError("MutableScalarAttributeImpl requires a copy function") self.copy = copy_function - def get_history(self, state, passive=False): + def get_history(self, state, passive=PASSIVE_OFF): return History.from_attribute( self, state, state.dict.get(self.key, NO_VALUE)) @@ -447,7 +465,7 @@ class MutableScalarAttributeImpl(ScalarAttributeImpl): dest[self.key] = self.copy(state.dict[self.key]) def check_mutable_modified(self, state): - (added, unchanged, deleted) = self.get_history(state, passive=True) + (added, unchanged, deleted) = self.get_history(state, passive=PASSIVE_NO_INITIALIZE) return bool(added or deleted) def set(self, state, value, initiator): @@ -487,7 +505,7 @@ class ScalarObjectAttributeImpl(ScalarAttributeImpl): self.fire_remove_event(state, old, self) del state.dict[self.key] - def get_history(self, state, passive=False): + def get_history(self, state, passive=PASSIVE_OFF): if self.key in state.dict: return History.from_attribute(self, state, state.dict[self.key]) else: @@ -569,7 +587,7 @@ class CollectionAttributeImpl(AttributeImpl): def __copy(self, item): return [y for y in list(collections.collection_adapter(item))] - def get_history(self, state, passive=False): + def get_history(self, state, passive=PASSIVE_OFF): current = self.get(state, passive=passive) if current is PASSIVE_NORESULT: return (None, None, None) @@ -577,7 +595,7 @@ class CollectionAttributeImpl(AttributeImpl): return History.from_attribute(self, state, current) def fire_append_event(self, state, value, initiator): - state.modified_event(self, True, NEVER_SET, passive=True) + state.modified_event(self, True, NEVER_SET, passive=PASSIVE_NO_INITIALIZE) if self.trackparent and value is not None: self.sethasparent(instance_state(value), True) @@ -587,10 +605,10 @@ class CollectionAttributeImpl(AttributeImpl): return value def fire_pre_remove_event(self, state, initiator): - state.modified_event(self, True, NEVER_SET, passive=True) + state.modified_event(self, True, NEVER_SET, passive=PASSIVE_NO_INITIALIZE) def fire_remove_event(self, state, value, initiator): - state.modified_event(self, True, NEVER_SET, passive=True) + state.modified_event(self, True, NEVER_SET, passive=PASSIVE_NO_INITIALIZE) if self.trackparent and value is not None: self.sethasparent(instance_state(value), False) @@ -620,7 +638,7 @@ class CollectionAttributeImpl(AttributeImpl): return state.manager.initialize_collection( self.key, state, self.collection_factory) - def append(self, state, value, initiator, passive=False): + def append(self, state, value, initiator, passive=PASSIVE_OFF): if initiator is self: return @@ -631,7 +649,7 @@ class CollectionAttributeImpl(AttributeImpl): else: collection.append_with_event(value, initiator) - def remove(self, state, value, initiator, passive=False): + def remove(self, state, value, initiator, passive=PASSIVE_OFF): if initiator is self: return @@ -721,7 +739,7 @@ class CollectionAttributeImpl(AttributeImpl): return user_data - def get_collection(self, state, user_data=None, passive=False): + def get_collection(self, state, user_data=None, passive=PASSIVE_OFF): """Retrieve the CollectionAdapter associated with the given state. Creates a new CollectionAdapter if one does not exist. @@ -754,23 +772,23 @@ class GenericBackrefExtension(interfaces.AttributeExtension): old_state = instance_state(oldchild) impl = old_state.get_impl(self.key) try: - impl.remove(old_state, state.obj(), initiator, passive=True) + impl.remove(old_state, state.obj(), initiator, passive=PASSIVE_NO_CALLABLES) except (ValueError, KeyError, IndexError): pass if child is not None: new_state = instance_state(child) - new_state.get_impl(self.key).append(new_state, state.obj(), initiator, passive=True) + new_state.get_impl(self.key).append(new_state, state.obj(), initiator, passive=PASSIVE_NO_CALLABLES) return child def append(self, state, child, initiator): child_state = instance_state(child) - child_state.get_impl(self.key).append(child_state, state.obj(), initiator, passive=True) + child_state.get_impl(self.key).append(child_state, state.obj(), initiator, passive=PASSIVE_NO_CALLABLES) return child def remove(self, state, child, initiator): if child is not None: child_state = instance_state(child) - child_state.get_impl(self.key).remove(child_state, state.obj(), initiator, passive=True) + child_state.get_impl(self.key).remove(child_state, state.obj(), initiator, passive=PASSIVE_NO_CALLABLES) class InstanceState(object): @@ -840,12 +858,12 @@ class InstanceState(object): self.pending[key] = PendingCollection() return self.pending[key] - def value_as_iterable(self, key, passive=False): + def value_as_iterable(self, key, passive=PASSIVE_OFF): """return an InstanceState attribute as a list, regardless of it being a scalar or collection-based attribute. - returns None if passive=True and the getter returns + returns None if passive is not PASSIVE_OFF and the getter returns PASSIVE_NORESULT. """ @@ -961,7 +979,7 @@ class InstanceState(object): self.dict.pop(key, None) self.callables.pop(key, None) - def modified_event(self, attr, should_copy, previous, passive=False): + def modified_event(self, attr, should_copy, previous, passive=PASSIVE_OFF): needs_committed = attr.key not in self.committed_state if needs_committed: diff --git a/lib/sqlalchemy/orm/properties.py b/lib/sqlalchemy/orm/properties.py index 473c71f7e..87e35eb83 100644 --- a/lib/sqlalchemy/orm/properties.py +++ b/lib/sqlalchemy/orm/properties.py @@ -495,8 +495,13 @@ class PropertyLoader(StrategizedProperty): def cascade_iterator(self, type_, state, visited_instances, halt_on=None): if not type_ in self.cascade: return + # only actively lazy load on the 'delete' cascade - passive = type_ != 'delete' or self.passive_deletes + if type_ != 'delete' or self.passive_deletes: + passive = attributes.PASSIVE_NO_INITIALIZE + else: + passive = attributes.PASSIVE_OFF + mapper = self.mapper.primary_mapper() instances = state.value_as_iterable(self.key, passive=passive) if instances: |
