diff options
| author | Mike Bayer <mike_mp@zzzcomputing.com> | 2010-03-29 17:56:02 -0400 |
|---|---|---|
| committer | Mike Bayer <mike_mp@zzzcomputing.com> | 2010-03-29 17:56:02 -0400 |
| commit | 75e14f855ee64a01bb79e66f8a868911f6c9e583 (patch) | |
| tree | 57e0fe600809b12db44040beed4b2e19cde22d6c /lib | |
| parent | f35132267e2245f44f44d94c25a3d2015c224ac2 (diff) | |
| download | sqlalchemy-75e14f855ee64a01bb79e66f8a868911f6c9e583.tar.gz | |
- Session.refresh() now does an equivalent expire()
on the given instance first, so that the "refresh-expire"
cascade is propagated. Previously, refresh() was
not affected in any way by the presence of "refresh-expire"
cascade. This is a change in behavior versus that
of 0.6beta2, where the "lockmode" flag passed to refresh()
would cause a version check to occur. Since the instance
is first expired, refresh() always upgrades the object
to the most recent version.
- The 'refresh-expire' cascade, when reaching a pending object,
will expunge the object if the cascade also includes
"delete-orphan", or will simply detach it otherwise.
[ticket:1754]
Diffstat (limited to 'lib')
| -rw-r--r-- | lib/sqlalchemy/orm/properties.py | 11 | ||||
| -rw-r--r-- | lib/sqlalchemy/orm/session.py | 27 | ||||
| -rw-r--r-- | lib/sqlalchemy/test/requires.py | 12 |
3 files changed, 42 insertions, 8 deletions
diff --git a/lib/sqlalchemy/orm/properties.py b/lib/sqlalchemy/orm/properties.py index a8295e2cd..2a5e92c1a 100644 --- a/lib/sqlalchemy/orm/properties.py +++ b/lib/sqlalchemy/orm/properties.py @@ -742,6 +742,8 @@ class RelationshipProperty(StrategizedProperty): else: instances = state.value_as_iterable(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 \ @@ -757,12 +759,17 @@ class RelationshipProperty(StrategizedProperty): str(self.parent.class_), str(c.__class__) )) + instance_state = attributes.instance_state(c) + + if skip_pending and not instance_state.key: + continue + visited_instances.add(c) # cascade using the mapper local to this # object, so that its individual properties are located - instance_mapper = object_mapper(c) - yield (c, instance_mapper, attributes.instance_state(c)) + instance_mapper = instance_state.manager.mapper + yield (c, instance_mapper, instance_state) def _add_reverse_property(self, key): other = self.mapper._get_property(key) diff --git a/lib/sqlalchemy/orm/session.py b/lib/sqlalchemy/orm/session.py index 0a3fbe79e..0810175bf 100644 --- a/lib/sqlalchemy/orm/session.py +++ b/lib/sqlalchemy/orm/session.py @@ -883,7 +883,7 @@ class Session(object): state.commit_all(dict_, self.identity_map) def refresh(self, instance, attribute_names=None, lockmode=None): - """Refresh the attributes on the given instance. + """Expire and refresh the attributes on the given instance. A query will be issued to the database and all attributes will be refreshed with their current database value. @@ -907,7 +907,9 @@ class Session(object): state = attributes.instance_state(instance) except exc.NO_STATE: raise exc.UnmappedInstanceError(instance) - self._validate_persistent(state) + + self._expire_state(state, attribute_names) + if self.query(_object_mapper(instance))._get( state.key, refresh_state=state, lockmode=lockmode, @@ -939,18 +941,31 @@ class Session(object): state = attributes.instance_state(instance) except exc.NO_STATE: raise exc.UnmappedInstanceError(instance) + self._expire_state(state, attribute_names) + + def _expire_state(self, state, attribute_names): self._validate_persistent(state) if attribute_names: _expire_state(state, state.dict, - attribute_names=attribute_names, instance_dict=self.identity_map) + attribute_names=attribute_names, + instance_dict=self.identity_map) else: # pre-fetch the full cascade since the expire is going to # remove associations cascaded = list(_cascade_state_iterator('refresh-expire', state)) - _expire_state(state, state.dict, None, instance_dict=self.identity_map) + self._conditional_expire(state) for (state, m, o) in cascaded: - _expire_state(state, state.dict, None, instance_dict=self.identity_map) - + self._conditional_expire(state) + + def _conditional_expire(self, state): + """Expire a state if persistent, else expunge if pending""" + + if state.key: + _expire_state(state, state.dict, None, instance_dict=self.identity_map) + elif state in self._new: + self._new.pop(state) + state.detach() + def prune(self): """Remove unreferenced instances cached in the identity map. diff --git a/lib/sqlalchemy/test/requires.py b/lib/sqlalchemy/test/requires.py index 73b212095..bf911c2c2 100644 --- a/lib/sqlalchemy/test/requires.py +++ b/lib/sqlalchemy/test/requires.py @@ -149,6 +149,18 @@ def sequences(fn): no_support('sybase', 'no SEQUENCE support'), ) +def update_nowait(fn): + """Target database must support SELECT...FOR UPDATE NOWAIT""" + return _chain_decorators_on( + fn, + no_support('access', 'no FOR UPDATE NOWAIT support'), + no_support('firebird', 'no FOR UPDATE NOWAIT support'), + no_support('mssql', 'no FOR UPDATE NOWAIT support'), + no_support('mysql', 'no FOR UPDATE NOWAIT support'), + no_support('sqlite', 'no FOR UPDATE NOWAIT support'), + no_support('sybase', 'no FOR UPDATE NOWAIT support'), + ) + def subqueries(fn): """Target database must support subqueries.""" return _chain_decorators_on( |
