From: Mike Bayer Date: Fri, 5 Sep 2008 17:16:11 +0000 (+0000) Subject: - rearranged delete() so that the object is attached before X-Git-Tag: rel_0_5rc1~14 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=f3cca5255b6dfaa0771a443c0119f5463ce9d56f;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git - rearranged delete() so that the object is attached before cascades fire off [ticket:5058] - after_attach() only fires if the object was not previously attached --- diff --git a/lib/sqlalchemy/orm/session.py b/lib/sqlalchemy/orm/session.py index f2d4e150a8..66009be01c 100644 --- a/lib/sqlalchemy/orm/session.py +++ b/lib/sqlalchemy/orm/session.py @@ -1144,12 +1144,28 @@ class Session(object): except exc.NO_STATE: raise exc.UnmappedInstanceError(instance) - # grab the full cascade list first, since lazyloads/autoflush - # may be triggered by this operation (delete cascade lazyloads by default) + if state.key is None: + raise sa_exc.InvalidRequestError( + "Instance '%s' is not persisted" % + mapperutil.state_str(state)) + + if state in self._deleted: + return + + # ensure object is attached to allow the + # cascade operation to load deferred attributes + # and collections + self._attach(state) + + # grab the cascades before adding the item to the deleted list + # so that autoflush does not delete the item cascade_states = list(_cascade_state_iterator('delete', state)) - self._delete_impl(state) + + self._deleted[state] = state.obj() + self.identity_map.add(state) + for state, m, o in cascade_states: - self._delete_impl(state, ignore_transient=True) + self._delete_impl(state) def merge(self, instance, dont_load=False, _recursive=None): @@ -1252,18 +1268,12 @@ class Session(object): if (self.identity_map.contains_state(state) and state not in self._deleted): return + if state.key is None: raise sa_exc.InvalidRequestError( "Instance '%s' is not persisted" % mapperutil.state_str(state)) - if (state.key in self.identity_map and - not self.identity_map.contains_state(state)): - raise sa_exc.InvalidRequestError( - "Could not update instance '%s', identity key %s; a different " - "instance with the same identity key already exists in this " - "session." % (mapperutil.state_str(state), state.key)) - self._attach(state) self._deleted.pop(state, None) self.identity_map.add(state) @@ -1274,38 +1284,36 @@ class Session(object): else: self._update_impl(state) - def _delete_impl(self, state, ignore_transient=False): - if self.identity_map.contains_state(state) and state in self._deleted: + def _delete_impl(self, state): + if state in self._deleted: return if state.key is None: - if ignore_transient: - return - else: - raise sa_exc.InvalidRequestError( - "Instance '%s' is not persisted" % - mapperutil.state_str(state)) - if (state.key in self.identity_map and - not self.identity_map.contains_state(state)): - raise sa_exc.InvalidRequestError( - "Instance '%s' is with key %s already persisted with a " - "different identity" % (mapperutil.state_str(state), - state.key)) - + return + self._attach(state) self._deleted[state] = state.obj() self.identity_map.add(state) - + def _attach(self, state): + if state.key and \ + state.key in self.identity_map and \ + not self.identity_map.contains_state(state): + raise sa_exc.InvalidRequestError( + "Can't attach instance %s; another instance with key %s is already present in this session." % + (mapperutil.state_str(state), state.key) + ) + if state.session_id and state.session_id is not self.hash_key: raise sa_exc.InvalidRequestError( "Object '%s' is already attached to session '%s' " "(this is '%s')" % (mapperutil.state_str(state), state.session_id, self.hash_key)) + if state.session_id != self.hash_key: state.session_id = self.hash_key - for ext in self.extensions: - ext.after_attach(self, state.obj()) + for ext in self.extensions: + ext.after_attach(self, state.obj()) def __contains__(self, instance): """Return True if the instance is associated with this session. diff --git a/test/orm/session.py b/test/orm/session.py index c2e6e9d15c..8dd98512e3 100644 --- a/test/orm/session.py +++ b/test/orm/session.py @@ -621,7 +621,10 @@ class SessionTest(_fixtures.FixtureTest): def test_save_update_delete(self): s = create_session() - mapper(User, users) + mapper(User, users, properties={ + 'addresses':relation(Address, cascade="all, delete") + }) + mapper(Address, addresses) user = User(name='u1') @@ -658,8 +661,9 @@ class SessionTest(_fixtures.FixtureTest): self.assertRaisesMessage(sa.exc.InvalidRequestError, "is already attached to session", s2.delete, user) u2 = s2.query(User).get(user.id) - self.assertRaisesMessage(sa.exc.InvalidRequestError, "already persisted with a different identity", s.delete, u2) + self.assertRaisesMessage(sa.exc.InvalidRequestError, "another instance with key", s.delete, u2) + s.expire(user) s.expunge(user) assert user not in s s.delete(user)