diff options
| author | Mike Bayer <mike_mp@zzzcomputing.com> | 2006-08-18 17:21:01 +0000 |
|---|---|---|
| committer | Mike Bayer <mike_mp@zzzcomputing.com> | 2006-08-18 17:21:01 +0000 |
| commit | c48177f0fed3a43b3b8b02c18243cb1664ce0abb (patch) | |
| tree | 1b4e70a8e4d64059fff906533cf14b35b8845946 /lib/sqlalchemy | |
| parent | bd04cffad83848e3edc5cb51c51ca6232270477a (diff) | |
| download | sqlalchemy-c48177f0fed3a43b3b8b02c18243cb1664ce0abb.tar.gz | |
- unit-of-work does a better check for "orphaned" objects that are
part of a "delete-orphan" cascade, for certain conditions where the
parent isnt available to cascade from.
- it is now invalid to declare a self-referential relationship with
"delete-orphan" (as the abovementioned check would make them impossible
to save)
- improved the check for objects being part of a session when the
unit of work seeks to flush() them as part of a relationship..
Diffstat (limited to 'lib/sqlalchemy')
| -rw-r--r-- | lib/sqlalchemy/orm/mapper.py | 8 | ||||
| -rw-r--r-- | lib/sqlalchemy/orm/properties.py | 5 | ||||
| -rw-r--r-- | lib/sqlalchemy/orm/session.py | 6 | ||||
| -rw-r--r-- | lib/sqlalchemy/orm/unitofwork.py | 12 |
4 files changed, 27 insertions, 4 deletions
diff --git a/lib/sqlalchemy/orm/mapper.py b/lib/sqlalchemy/orm/mapper.py index ab0226a41..2347da2ba 100644 --- a/lib/sqlalchemy/orm/mapper.py +++ b/lib/sqlalchemy/orm/mapper.py @@ -86,6 +86,7 @@ class Mapper(object): self.properties = properties or {} self.allow_column_override = allow_column_override self.allow_null_pks = allow_null_pks + self.delete_orphans = [] # a Column which is used during a select operation to retrieve the # "polymorphic identity" of the row, which indicates which Mapper should be used @@ -139,6 +140,13 @@ class Mapper(object): # of dependency #self.compile() + def _is_orphan(self, obj): + for (key,klass) in self.delete_orphans: + if not getattr(klass, key).hasparent(obj): + return True + else: + return False + def _get_props(self): self.compile() return self.__props diff --git a/lib/sqlalchemy/orm/properties.py b/lib/sqlalchemy/orm/properties.py index b5d66c160..16760dc06 100644 --- a/lib/sqlalchemy/orm/properties.py +++ b/lib/sqlalchemy/orm/properties.py @@ -216,6 +216,11 @@ class PropertyLoader(mapper.MapperProperty): self.target = self.mapper.mapped_table + if self.cascade.delete_orphan: + if self.parent.class_ is self.mapper.class_: + raise exceptions.ArgumentError("Cant establish 'delete-orphan' cascade rule on a self-referential relationship. You probably want cascade='all', which includes delete cascading but not orphan detection.") + self.mapper.primary_mapper().delete_orphans.append((self.key, self.parent.class_)) + if self.secondaryjoin is not None and self.secondary is None: raise exceptions.ArgumentError("Property '" + self.key + "' specified with secondary join condition but no secondary argument") # if join conditions were not specified, figure them out based on foreign keys diff --git a/lib/sqlalchemy/orm/session.py b/lib/sqlalchemy/orm/session.py index 5c962aa39..e5c95cbe4 100644 --- a/lib/sqlalchemy/orm/session.py +++ b/lib/sqlalchemy/orm/session.py @@ -350,6 +350,12 @@ class Session(object): raise exceptions.InvalidRequestError("Instance '%s' is a detached instance or is already persistent in a different Session" % repr(object)) else: m = class_mapper(object.__class__, entity_name=kwargs.get('entity_name', None)) + + # this would be a nice exception to raise...however this is incompatible with a contextual + # session which puts all objects into the session upon construction. + #if m._is_orphan(object): + # raise exceptions.InvalidRequestError("Instance '%s' is an orphan, and must be attached to a parent object to be saved" % (repr(object))) + m._assign_entity_name(object) self._register_new(object) diff --git a/lib/sqlalchemy/orm/unitofwork.py b/lib/sqlalchemy/orm/unitofwork.py index 33e0149e4..00d34a104 100644 --- a/lib/sqlalchemy/orm/unitofwork.py +++ b/lib/sqlalchemy/orm/unitofwork.py @@ -106,9 +106,10 @@ class UnitOfWork(object): pass def _validate_obj(self, obj): - if hasattr(obj, '_instance_key') and not self.identity_map.has_key(obj._instance_key): - raise InvalidRequestError("Instance '%s' is not attached or pending within this session" % repr(obj._instance_key)) - + if (hasattr(obj, '_instance_key') and not self.identity_map.has_key(obj._instance_key)) or \ + (not hasattr(obj, '_instance_key') and obj not in self.new): + raise InvalidRequestError("Instance '%s' is not attached or pending within this session" % repr(obj)) + def update(self, obj): """called to add an object to this UnitOfWork as though it were loaded from the DB, but is actually coming from somewhere else, like a web session or similar.""" @@ -184,7 +185,10 @@ class UnitOfWork(object): continue if obj in self.deleted: continue - flush_context.register_object(obj) + if object_mapper(obj)._is_orphan(obj): + flush_context.register_object(obj, isdelete=True) + else: + flush_context.register_object(obj) for obj in self.deleted: if objset is not None and not obj in objset: |
