From: Mike Bayer Date: Tue, 21 Feb 2006 02:57:38 +0000 (+0000) Subject: working on postupdate idea, refactoring to dependency processing X-Git-Tag: rel_0_1_1~11 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=a2ff73200cd6bfa465d13b8531c2745007ebf8c9;p=thirdparty%2Fsqlalchemy%2Fsqlalchemy.git working on postupdate idea, refactoring to dependency processing --- diff --git a/lib/sqlalchemy/mapping/__init__.py b/lib/sqlalchemy/mapping/__init__.py index 64258fa654..2de644f43b 100644 --- a/lib/sqlalchemy/mapping/__init__.py +++ b/lib/sqlalchemy/mapping/__init__.py @@ -88,21 +88,6 @@ def undefer(name, **kwargs): name into a non-deferred (regular column) load. Used with mapper.options.""" return DeferredOption(name, defer=False) -def object_mapper(object): - """given an object, returns the primary Mapper associated with the object - or the object's class.""" - return class_mapper(object.__class__) - -def class_mapper(class_): - """given a class, returns the primary Mapper associated with the class.""" - return mapper_registry[class_] - try: - return mapper_registry[class_] - except KeyError: - pass - except AttributeError: - pass - raise InvalidRequestError("Class '%s' has no mapper associated with it" % class_.__name__) def assign_mapper(class_, *args, **params): diff --git a/lib/sqlalchemy/mapping/mapper.py b/lib/sqlalchemy/mapping/mapper.py index 88b803b37e..2e69a1e661 100644 --- a/lib/sqlalchemy/mapping/mapper.py +++ b/lib/sqlalchemy/mapping/mapper.py @@ -893,6 +893,22 @@ def hash_key(obj): return obj.hash_key() else: return repr(obj) + +def object_mapper(object): + """given an object, returns the primary Mapper associated with the object + or the object's class.""" + return class_mapper(object.__class__) + +def class_mapper(class_): + """given a class, returns the primary Mapper associated with the class.""" + return mapper_registry[class_] + try: + return mapper_registry[class_] + except KeyError: + pass + except AttributeError: + pass + raise InvalidRequestError("Class '%s' has no mapper associated with it" % class_.__name__) diff --git a/lib/sqlalchemy/mapping/objectstore.py b/lib/sqlalchemy/mapping/objectstore.py index 94e2f966b0..e68c59e30d 100644 --- a/lib/sqlalchemy/mapping/objectstore.py +++ b/lib/sqlalchemy/mapping/objectstore.py @@ -454,8 +454,9 @@ class UOWTransaction(object): self.dependencies = {} self.tasks = {} self.saved_histories = util.HashSet() - - def register_object(self, obj, isdelete = False, listonly = False, **kwargs): + self.__modified = False + + def register_object(self, obj, isdelete = False, listonly = False, postupdate=False, **kwargs): """adds an object to this UOWTransaction to be updated in the database. 'isdelete' indicates whether the object is to be deleted or saved (update/inserted). @@ -474,15 +475,19 @@ class UOWTransaction(object): self.mappers.append(mapper) task = self.get_task_by_mapper(mapper) + if postupdate: + mod = task.append_postupdate(obj) + self.__modified = self.__modified or mod + return + # for a cyclical task, things need to be sorted out already, # so this object should have already been added to the appropriate sub-task # can put an assertion here to make sure.... if task.circular: return - if obj not in task.objects: - self.__modified = True - task.append(obj, listonly, isdelete=isdelete, **kwargs) + mod = task.append(obj, listonly, isdelete=isdelete, **kwargs) + self.__modified = self.__modified or mod def unregister_object(self, obj): mapper = object_mapper(obj) @@ -665,16 +670,24 @@ class UOWTask(object): dependent operations at the per-object instead of per-task level. """ try: rec = self.objects[obj] + retval = False except KeyError: rec = UOWTaskElement(obj) self.objects[obj] = rec + retval = True if not listonly: rec.listonly = False if childtask: rec.childtasks.append(childtask) if isdelete: rec.isdelete = True - + return retval + + def append_postupdate(self, obj): + # postupdates are UPDATED immeditely (for now) + self.mapper.save_obj([obj], self.uowtransaction, postupdate=True) + return True + def delete(self, obj): try: del self.objects[obj] @@ -974,6 +987,9 @@ class UOWTask(object): for child in self.childtasks: header(buf, _indent() + " |- Child tasks\n") child._dump(buf, indent + 1) +# for obj in self.postupdate: +# header(buf, _indent() + " |- Post Update objects\n") +# buf.write(_repr(obj) + "\n") for element in self.todelete_elements(): for task in element.childtasks: header(buf, _indent() + " |- Delete subelements of UOWTaskElement(%s)\n" % id(element)) diff --git a/lib/sqlalchemy/mapping/properties.py b/lib/sqlalchemy/mapping/properties.py index 571bfc0b57..036dfe4400 100644 --- a/lib/sqlalchemy/mapping/properties.py +++ b/lib/sqlalchemy/mapping/properties.py @@ -358,14 +358,20 @@ class PropertyLoader(MapperProperty): uowcommit.register_processor(stub, self, self.parent, True) elif self.direction == PropertyLoader.ONETOMANY: if self.post_update: - raise InvalidRequestError("post_update not yet supported with one-to-many relation") - uowcommit.register_dependency(self.parent, self.mapper) - uowcommit.register_processor(self.parent, self, self.parent, False) - uowcommit.register_processor(self.parent, self, self.parent, True) + stub = PropertyLoader.MapperStub(self.mapper) + uowcommit.register_dependency(self.mapper, stub) + uowcommit.register_dependency(self.parent, stub) + uowcommit.register_processor(stub, self, self.parent, False) + uowcommit.register_processor(stub, self, self.parent, True) + else: + uowcommit.register_dependency(self.parent, self.mapper) + uowcommit.register_processor(self.parent, self, self.parent, False) + uowcommit.register_processor(self.parent, self, self.parent, True) elif self.direction == PropertyLoader.MANYTOONE: if self.post_update: stub = PropertyLoader.MapperStub(self.mapper) uowcommit.register_dependency(self.mapper, stub) + uowcommit.register_dependency(self.parent, stub) uowcommit.register_processor(stub, self, self.parent, False) uowcommit.register_processor(stub, self, self.parent, True) else: @@ -433,12 +439,12 @@ class PropertyLoader(MapperProperty): statement = self.secondary.insert() statement.execute(*secondary_insert) elif self.direction == PropertyLoader.MANYTOONE and delete: - # head object is being deleted, and we manage a foreign key object. - # dont have to do anything to it. if self.post_update: + # post_update means we have to update our row to not reference the child object + # before we can DELETE the row for obj in deplist: self._synchronize(obj, None, None, True) - task.mapper.save_obj([obj], uowcommit, postupdate=True) + uowcommit.register_object(obj, postupdate=True) elif self.direction == PropertyLoader.ONETOMANY and delete: # head object is being deleted, and we manage its list of child objects # the child objects have to have their foreign key to the parent set to NULL @@ -450,7 +456,7 @@ class PropertyLoader(MapperProperty): childlist = getlist(obj, False) for child in childlist.deleted_items() + childlist.unchanged_items(): self._synchronize(obj, child, None, True) - uowcommit.register_object(child) + uowcommit.register_object(child, postupdate=self.post_update) elif self.association is not None: # manage association objects. for obj in deplist: @@ -488,21 +494,16 @@ class PropertyLoader(MapperProperty): #print "DELETE ASSOC OBJ", repr(child) uowcommit.register_object(child, isdelete=True) else: - for obj in deplist: - if self.direction == PropertyLoader.MANYTOONE: - uowcommit.register_object(obj) + for obj in deplist: childlist = getlist(obj, passive=True) - if childlist is None: continue - for child in childlist.added_items(): - self._synchronize(obj, child, None, False) - if self.direction == PropertyLoader.ONETOMANY and child is not None: - if self.post_update: - task.mapper.save_obj([child],uowcommit, postupdate=True) - else: - uowcommit.register_object(child) - if self.post_update: - task.mapper.save_obj([obj], uowcommit, postupdate=True) - if self.direction != PropertyLoader.MANYTOONE or len(childlist.added_items()) == 0: + if childlist is not None: + for child in childlist.added_items(): + self._synchronize(obj, child, None, False) + if self.direction == PropertyLoader.ONETOMANY and child is not None: + uowcommit.register_object(child, postupdate=self.post_update) + if self.direction == PropertyLoader.MANYTOONE: + uowcommit.register_object(obj, postupdate=self.post_update) + if self.direction != PropertyLoader.MANYTOONE: for child in childlist.deleted_items(): if not self.private: self._synchronize(obj, child, None, True) diff --git a/test/cycles.py b/test/cycles.py index 863d30fd95..f30ac0297b 100644 --- a/test/cycles.py +++ b/test/cycles.py @@ -238,8 +238,8 @@ class CycleTest2(AssertMixin): Ball.mapper = mapper(Ball, ball) Person.mapper = mapper(Person, person, properties= dict( - balls = relation(Ball.mapper, primaryjoin=ball.c.person_id==person.c.id, foreignkey=ball.c.person_id), - favorateBall = relation(Ball.mapper, primaryjoin=person.c.favoriteBall_id==ball.c.id, foreignkey=person.c.favoriteBall_id), + balls = relation(Ball.mapper, primaryjoin=ball.c.person_id==person.c.id, foreignkey=ball.c.person_id, post_update=False, private=True), + favorateBall = relation(Ball.mapper, primaryjoin=person.c.favoriteBall_id==ball.c.id, foreignkey=person.c.favoriteBall_id, post_update=True), ) ) @@ -248,8 +248,14 @@ class CycleTest2(AssertMixin): b = Ball() p = Person() p.balls.append(b) + p.balls.append(Ball()) + p.balls.append(Ball()) + p.balls.append(Ball()) p.favorateBall = b objectstore.commit() + + objectstore.delete(p) + objectstore.commit() if __name__ == "__main__":