diff options
| author | Mike Bayer <mike_mp@zzzcomputing.com> | 2007-10-30 18:04:00 +0000 |
|---|---|---|
| committer | Mike Bayer <mike_mp@zzzcomputing.com> | 2007-10-30 18:04:00 +0000 |
| commit | 206c0b9792b02a0d77d92e679952cb2d0465cede (patch) | |
| tree | 9927d4f396f1f3d99982ef9e2dab40753f74d970 /lib/sqlalchemy | |
| parent | adcf8ea00dda6d8e62fceeab36d90eabe36b5f91 (diff) | |
| download | sqlalchemy-206c0b9792b02a0d77d92e679952cb2d0465cede.tar.gz | |
- fix to "row switch" behavior, i.e. when an INSERT/DELETE is combined into a
single UPDATE; many-to-many relations on the parent object update properly.
[ticket:841]
- it's an error to session.save() an object which is already persistent
[ticket:840]
- changed a bunch of repr(obj) calls in session.py exceptions to use mapperutil.instance_str()
Diffstat (limited to 'lib/sqlalchemy')
| -rw-r--r-- | lib/sqlalchemy/orm/mapper.py | 2 | ||||
| -rw-r--r-- | lib/sqlalchemy/orm/session.py | 17 | ||||
| -rw-r--r-- | lib/sqlalchemy/orm/unitofwork.py | 13 |
3 files changed, 21 insertions, 11 deletions
diff --git a/lib/sqlalchemy/orm/mapper.py b/lib/sqlalchemy/orm/mapper.py index 2f3c36515..94ef04300 100644 --- a/lib/sqlalchemy/orm/mapper.py +++ b/lib/sqlalchemy/orm/mapper.py @@ -968,7 +968,7 @@ class Mapper(object): raise exceptions.FlushError("New instance %s with identity key %s conflicts with persistent instance %s" % (mapperutil.instance_str(obj), str(instance_key), mapperutil.instance_str(existing))) if self.__should_log_debug: self.__log_debug("detected row switch for identity %s. will update %s, remove %s from transaction" % (instance_key, mapperutil.instance_str(obj), mapperutil.instance_str(existing))) - uowtransaction.unregister_object(existing) + uowtransaction.set_row_switch(existing) if has_identity(obj): if obj._instance_key != instance_key: raise exceptions.FlushError("Can't change the identity of instance %s in session (existing identity: %s; new identity: %s)" % (mapperutil.instance_str(obj), obj._instance_key, instance_key)) diff --git a/lib/sqlalchemy/orm/session.py b/lib/sqlalchemy/orm/session.py index 04aaa1209..42ad30b6a 100644 --- a/lib/sqlalchemy/orm/session.py +++ b/lib/sqlalchemy/orm/session.py @@ -631,6 +631,8 @@ class Session(object): if self.bind is not None: return self.bind + elif mapper is None: + raise exceptions.InvalidRequestError("Could not locate any mapper associated with SQL expression") else: if isinstance(mapper, type): mapper = _class_mapper(mapper) @@ -721,7 +723,7 @@ class Session(object): self._validate_persistent(obj) if self.query(obj.__class__)._get(obj._instance_key, reload=True) is None: - raise exceptions.InvalidRequestError("Could not refresh instance '%s'" % repr(obj)) + raise exceptions.InvalidRequestError("Could not refresh instance '%s'" % mapperutil.instance_str(obj)) def expire(self, obj): """Mark the given object as expired. @@ -753,7 +755,7 @@ class Session(object): def exp(): if self.query(obj.__class__)._get(obj._instance_key, reload=True) is None: - raise exceptions.InvalidRequestError("Could not refresh instance '%s'" % repr(obj)) + raise exceptions.InvalidRequestError("Could not refresh instance '%s'" % mapperutil.instance_str(obj)) attribute_manager.trigger_history(obj, exp) @@ -954,10 +956,7 @@ class Session(object): def _save_impl(self, obj, **kwargs): if hasattr(obj, '_instance_key'): - if obj._instance_key not in self.identity_map: - raise exceptions.InvalidRequestError("Instance '%s' is a detached instance " - "or is already persistent in a " - "different Session" % repr(obj)) + raise exceptions.InvalidRequestError("Instance '%s' is already persistent" % mapperutil.instance_str(obj)) else: # TODO: consolidate the steps here attribute_manager.manage(obj) @@ -969,7 +968,7 @@ class Session(object): if self._is_attached(obj) and obj not in self.deleted: return if not hasattr(obj, '_instance_key'): - raise exceptions.InvalidRequestError("Instance '%s' is not persisted" % repr(obj)) + raise exceptions.InvalidRequestError("Instance '%s' is not persisted" % mapperutil.instance_str(obj)) self._attach(obj) def _register_persistent(self, obj): @@ -983,7 +982,7 @@ class Session(object): if old_id is not None and old_id in _sessions: raise exceptions.InvalidRequestError("Object '%s' is already attached " "to session '%s' (this is '%s')" % - (repr(obj), old_id, id(self))) + (mapperutil.instance_str(obj), old_id, id(self))) # auto-removal from the old session is disabled. but if we decide to # turn it back on, do it as below: gingerly since _sessions is a WeakValueDict @@ -1001,7 +1000,7 @@ class Session(object): def _unattach(self, obj): if not self._is_attached(obj): - raise exceptions.InvalidRequestError("Instance '%s' not attached to this Session" % repr(obj)) + raise exceptions.InvalidRequestError("Instance '%s' not attached to this Session" % mapperutil.instance_str(obj)) del obj._sa_session_id def _validate_persistent(self, obj): diff --git a/lib/sqlalchemy/orm/unitofwork.py b/lib/sqlalchemy/orm/unitofwork.py index 7a443b331..43f0d46d9 100644 --- a/lib/sqlalchemy/orm/unitofwork.py +++ b/lib/sqlalchemy/orm/unitofwork.py @@ -304,6 +304,17 @@ class UOWTransaction(object): task.append(obj, listonly, isdelete=isdelete, **kwargs) + def set_row_switch(self, obj): + """mark a deleted object as a 'row switch'. + + this indicates that an INSERT statement elsewhere corresponds to this DELETE; + the INSERT is converted to an UPDATE and the DELETE does not occur. + """ + mapper = object_mapper(obj) + task = self.get_task_by_mapper(mapper) + taskelement = task._objects[obj] + taskelement.isdelete = "rowswitch" + def unregister_object(self, obj): """remove an object from its parent UOWTask. @@ -902,7 +913,7 @@ class UOWTaskElement(object): self.childtasks = [] self.__isdelete = False self.__preprocessed = {} - + def _get_listonly(self): return self.__listonly |
