]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
(no commit message)
authorMike Bayer <mike_mp@zzzcomputing.com>
Sun, 18 Sep 2005 23:30:50 +0000 (23:30 +0000)
committerMike Bayer <mike_mp@zzzcomputing.com>
Sun, 18 Sep 2005 23:30:50 +0000 (23:30 +0000)
lib/sqlalchemy/attributes.py
lib/sqlalchemy/mapper.py
lib/sqlalchemy/objectstore.py
test/objectstore.py

index 06ed30605ee0ce457f88910f5e7bfe82f677d5fd..959a39ad949e4556e2ae4b3a113ea26d9534330f 100644 (file)
@@ -148,6 +148,7 @@ class AttributeManager(object):
         self.get_history(obj, key).delattr()
         self.value_changed(obj, key, value)
 
+        
     def delete_list_attribute(self, obj, key):
         pass
         
@@ -176,7 +177,13 @@ class AttributeManager(object):
                     hist.commit()
             except KeyError:
                 pass
-
+                
+    def remove(self, obj):
+        try:
+            del self.attribute_history[obj]
+        except KeyError:
+            pass
+            
     def get_history(self, obj, key):
         try:
             return self.attribute_history[obj][key]
@@ -192,12 +199,14 @@ class AttributeManager(object):
                 self.attribute_history[obj][key] = p
                 return p
 
-    def get_list_history(self, obj, key):
+    def get_list_history(self, obj, key, passive = False):
         try:
             return self.attribute_history[obj][key]
         except KeyError, e:
             list_ = obj.__dict__.get(key, None)
             if callable(list_):
+                if passive:
+                    return None
                 list_ = list_()
             if e.args[0] is obj:
                 d = {}
index 53f4596d993514591f27304ec43d6bdc5a0a3937..b56a2753da7d1eedd82a40fa85e936af0b44cce9 100644 (file)
@@ -286,7 +286,25 @@ class Mapper(object):
                                 found = True
 
     def delete_obj(self, objects, uow):
-        pass
+        for table in self.tables:
+            delete = []
+            for obj in objects:
+                params = {}
+                if not hasattr(obj, "_instance_key"):
+                    continue
+                else:
+                    delete.append(params)
+                for col in table.primary_keys:
+                    params[col.key] = self._getattrbycolumn(obj, col)
+                uow.register_deleted_object(obj)
+            if len(delete):
+                clause = sql.and_()
+                for col in self.primary_keys[table]:
+                    clause.clauses.append(col == sql.bindparam(col.key))
+                statement = table.delete(clause)
+                c = statement.execute(*delete)
+                if c.rowcount != len(delete):
+                    raise "ConcurrencyError - updated rowcount does not match number of objects updated"
 
     def register_dependencies(self, *args, **kwargs):
         for prop in self.props.values():
@@ -517,57 +535,52 @@ class PropertyLoader(MapperProperty):
             # if only a list changes, the parent mapper is the only mapper that
             # gets added to the "todo" list
             uowcommit.register_dependency(self.mapper, self.parent)
-            uowcommit.register_task(self.parent, self, uowcommit.get_objects(self.parent), False)
+            uowcommit.register_task(self.parent, False, self, self.parent, False)
         elif self.foreignkey.table == self.target:
             uowcommit.register_dependency(self.parent, self.mapper)
-            uowcommit.register_task(self.parent, self, uowcommit.get_objects(self.parent), False)
+            uowcommit.register_task(self.parent, False, self, self.parent, False)
+            uowcommit.register_task(self.parent, True, self, self.mapper, False)
+                
         elif self.foreignkey.table == self.parent.table:
             uowcommit.register_dependency(self.mapper, self.parent)
-            uowcommit.register_task(self.mapper, self, uowcommit.get_objects(self.parent), False)
+            uowcommit.register_task(self.mapper, False, self, self.parent, False)
+            #uowcommit.register_task(self.mapper, True, self, self.parent, False)
         else:
             raise " no foreign key ?"
                 
     def process_dependencies(self, deplist, uowcommit, delete = False):
-        print self.mapper.table.name + " process_dep"
-        def getlist(obj):
+        print self.mapper.table.name + " process_dep isdelete " + repr(delete)
+        def getlist(obj, passive = True):
             if self.uselist:
-                return uowcommit.uow.attributes.get_list_history(obj, self.key)
+                return uowcommit.uow.attributes.get_list_history(obj, self.key, passive = passive)
             else: 
                 return uowcommit.uow.attributes.get_history(obj, self.key)
 
-        clearkeys = False
-        
         def sync_foreign_keys(binary):
             self._sync_foreign_keys(binary, obj, child, associationrow, clearkeys)
         setter = BinaryVisitor(sync_foreign_keys)
 
+        associationrow = {}
+        
         if self.secondaryjoin is not None:
             secondary_delete = []
             secondary_insert = []
             for obj in deplist:
                 childlist = getlist(obj)
-                if delete:
-                    clearkeys = True
-                    for child in childlist.deleted_items() + childlist.unchanged_items():
-                        associationrow = {}
-                        self.primaryjoin.accept_visitor(setter)
-                        self.secondaryjoin.accept_visitor(setter)
-                        secondary_delete.append(associationrow)
-                    uowcommit.register_removed_list(childlist)
-                else:
-                    clearkeys = False
-                    for child in childlist.added_items():
-                        associationrow = {}
-                        self.primaryjoin.accept_visitor(setter)
-                        self.secondaryjoin.accept_visitor(setter)
-                        secondary_insert.append(associationrow)
-                    clearkeys = True
-                    for child in childlist.deleted_items():
-                        associationrow = {}
-                        self.primaryjoin.accept_visitor(setter)
-                        self.secondaryjoin.accept_visitor(setter)
-                        secondary_delete.append(associationrow)
-                    uowcommit.register_saved_list(childlist)
+                if childlist is None: return
+                clearkeys = False
+                for child in childlist.added_items():
+                    associationrow = {}
+                    self.primaryjoin.accept_visitor(setter)
+                    self.secondaryjoin.accept_visitor(setter)
+                    secondary_insert.append(associationrow)
+                clearkeys = True
+                for child in childlist.deleted_items():
+                    associationrow = {}
+                    self.primaryjoin.accept_visitor(setter)
+                    self.secondaryjoin.accept_visitor(setter)
+                    secondary_delete.append(associationrow)
+                uowcommit.register_saved_list(childlist)
             if len(secondary_delete):
                 statement = self.secondary.delete(sql.and_(*[c == sql.bindparam(c.key) for c in self.secondary.c]))
                 statement.execute(*secondary_delete)
@@ -575,40 +588,38 @@ class PropertyLoader(MapperProperty):
                 statement = self.secondary.insert()
                 statement.execute(*secondary_insert)
         elif self.foreignkey.table == self.target:
-            associationrow = {}
-            for obj in deplist:
-                childlist = getlist(obj)
-                if delete:
-                    clearkeys = True
-                    for child in childlist.deleted_items() + childlist.current_items():
-                        self.primaryjoin.accept_visitor(setter)
-                        uowcommit.register_saved_list(childlist)
-                else:
+            if delete:
+                updates = []
+                for obj in deplist:
+                    childlist = getlist(obj, False)
+                #if len(updates):
+                #    statement = self.secondary.update(sql.and_(*[c == sql.bindparam(c.key) for c in self.secondary.c]))
+                #    statement.execute(*secondary_delete)
+            else:
+                for obj in deplist:
+                    childlist = getlist(obj)
+                    if childlist is None: return
+                    uowcommit.register_saved_list(childlist)
                     clearkeys = False
                     for child in childlist.added_items():
                         self.primaryjoin.accept_visitor(setter)
-                        uowcommit.register_saved_list(childlist)
                     clearkeys = True
                     for child in childlist.deleted_items():
                          self.primaryjoin.accept_visitor(setter)
-                         uowcommit.register_saved_list(childlist)
         elif self.foreignkey.table == self.parent.table:
-            associationrow = {}
             for child in deplist:
                 childlist = getlist(child)
-                if delete:
-                    for obj in childlist.deleted_items() + childlist.current_items():
+                if childlist is None: return
+                uowcommit.register_saved_list(childlist)
+                clearkeys = False
+                added = childlist.added_items()
+                if len(added):
+                    for obj in added:
                         self.primaryjoin.accept_visitor(setter)
-                        uowcommit.register_saved_list(childlist)
                 else:
-                    clearkeys = False
-                    for obj in childlist.added_items():
-                        self.primaryjoin.accept_visitor(setter)
-                        uowcommit.register_saved_list(childlist)
                     clearkeys = True
                     for obj in childlist.deleted_items():
                         self.primaryjoin.accept_visitor(setter)
-                        uowcommit.register_saved_list(childlist)
         else:
             raise " no foreign key ?"
 
@@ -629,10 +640,9 @@ class PropertyLoader(MapperProperty):
             elif colmap.has_key(self.target) and colmap.has_key(self.secondary):
                 associationrow[colmap[self.secondary].key] = self.mapper._getattrbycolumn(child, colmap[self.target])
             
-    def delete(self):
-        self.mapper.delete()
-
 
+# TODO: break out the lazywhere capability so that the main PropertyLoader can use it
+# to do child deletes
 class LazyLoader(PropertyLoader):
 
     def init(self, key, parent):
index 2b00e7d66fc8fd60d598b818820507bc5b7b1d65..a22178fcab075c77aa3822c18965ffe479e067d6 100644 (file)
@@ -105,6 +105,12 @@ class UnitOfWork(object):
         
     def _put(self, key, obj):
         self.identity_map[key] = obj
+    
+    def _remove_deleted(self, obj):
+        if hasattr(obj, "_instancekey"):
+            del self.identity_map[obj._instancekey]
+        del self.deleted[obj]
+        self.attributes.remove(obj)
         
     def update(self, obj):
         """called to add an object to this UnitOfWork as though it were loaded from the DB, but is
@@ -207,13 +213,16 @@ class UnitOfWork(object):
 class UOWTransaction(object):
     def __init__(self, uow):
         self.uow = uow
+        # links objects to their mappers
         self.object_mappers = {}
+        #  unique list of all the mappers we come across
         self.mappers = util.HashSet()
         self.dependencies = {}
         self.tasks = {}
         self.saved_objects = util.HashSet()
         self.saved_lists = util.HashSet()
         self.deleted_objects = util.HashSet()
+        self.deleted_lists = util.HashSet()
 
     def append_task(self, obj):
         mapper = self.object_mapper(obj)
@@ -222,8 +231,8 @@ class UOWTransaction(object):
 
     def add_item_to_delete(self, obj):
         mapper = self.object_mapper(obj)
-        task = self.get_task_by_mapper(mapper)
-        task.todelete.append(obj)
+        task = self.get_task_by_mapper(mapper, True)
+        task.objects.append(obj)
 
     def get_task_by_mapper(self, mapper, isdelete = False):
         try:
@@ -239,13 +248,13 @@ class UOWTransaction(object):
             
         return task.objects
             
-    # TODO: better interface for tasks with no object save, or multiple dependencies
     def register_dependency(self, mapper, dependency):
         self.dependencies[(mapper, dependency)] = True
 
-    def register_task(self, mapper, processor, objects, isdelete):
+    def register_task(self, mapper, isdelete, processor, mapperfrom, isdeletefrom):
         task = self.get_task_by_mapper(mapper, isdelete)
-        task.dependencies.append((processor, objects))
+        targettask = self.get_task_by_mapper(mapperfrom, isdeletefrom)
+        task.dependencies.append((processor, targettask))
 
     def register_saved_object(self, obj):
         self.saved_objects.append(obj)
@@ -253,7 +262,10 @@ class UOWTransaction(object):
     def register_saved_list(self, listobj):
         self.saved_lists.append(listobj)
 
-    def register_deleted(self, obj):
+    def register_deleted_list(self, listobj):
+        self.deleted_lists.append(listobj)
+        
+    def register_deleted_object(self, obj):
         self.deleted_objects.append(obj)
         
         
@@ -273,24 +285,33 @@ class UOWTransaction(object):
             
         tasklist = self.tasks.values()
         def compare(a, b):
-            if self.dependencies.has_key((a.mapper, b.mapper)):
-                return -1
+            if a.mapper is b.mapper:
+                return a.isdelete and 1 or -1
+            elif self.dependencies.has_key((a.mapper, b.mapper)):
+                if a.isdelete is not b.isdelete:
+                    return a.isdelete and 1 or -1
+                else:
+                    return -1
             elif self.dependencies.has_key((b.mapper, a.mapper)):
-                return 1
+                if a.isdelete is not b.isdelete:
+                    return a.isdelete and 1 or -1
+                else:
+                    return 1
             else:
                 return 0
+            return c
         tasklist.sort(compare)
 
         import string
+        print string.join([str(t) for t in tasklist], ',')
+
         for task in tasklist:
             obj_list = task.objects
-            if len(obj_list):
-                print "t:" + string.join([o.__class__.__name__ for o in obj_list])
             if not task.isdelete:
                 task.mapper.save_obj(obj_list, self)
             for dep in task.dependencies:
-                (processor, stuff) = dep
-                processor.process_dependencies(stuff, self, delete = task.isdelete)
+                (processor, targettask) = dep
+                processor.process_dependencies(targettask.objects, self, delete = task.isdelete)
             if task.isdelete:
                 task.mapper.delete_obj(obj_list, self)
             
@@ -306,12 +327,26 @@ class UOWTransaction(object):
             except KeyError:
                 pass
 
+        for obj in self.deleted_objects:
+            self.uow._remove_deleted(obj)
         
+        for obj in self.deleted_lists:
+            try:
+                del self.uow.modified_lists[obj]
+            except KeyError:
+                pass
+            
 class UOWTask(object):
     def __init__(self, mapper, isdelete = False):
         self.mapper = mapper
         self.isdelete = isdelete
         self.objects = util.HashSet()
         self.dependencies = []
-        
+    
+    def __str__(self):
+        if self.isdelete:
+            return self.mapper.table.name + " deletes"
+        else:
+            return self.mapper.table.name + " saves"
+            
 uow = util.ScopedRegistry(lambda: UnitOfWork(), "thread")
\ No newline at end of file
index 262fa0c465c0a47f775a02e3df9502aeecbd2970..1c14f9aaaf928062b8d81f340c101e54ca918f88 100644 (file)
@@ -126,6 +126,20 @@ class SaveTest(AssertMixin):
         u.address.email_address = 'imnew@foo.com'
         objectstore.uow().commit()
 
+    def testdelete(self):
+        m = mapper(User, users, properties = dict(
+            address = relation(Address, addresses, lazy = True, uselist = False)
+        ))
+        u = User()
+        u.user_name = 'one2onetester'
+        u.address = Address()
+        u.address.email_address = 'myonlyaddress@foo.com'
+        objectstore.uow().commit()
+
+        print "OK"
+        objectstore.uow().register_deleted(u)
+        objectstore.uow().commit()
+        
     def testbackwardsonetoone(self):
         # test 'backwards'
 #        m = mapper(Address, addresses, properties = dict(
@@ -151,7 +165,6 @@ class SaveTest(AssertMixin):
             objects.append(a)
             
         objectstore.uow().commit()
-        
         objects[2].email_address = 'imnew@foo.bar'
         objects[3].user = User()
         objects[3].user.user_name = 'imnewlyadded'