1. lecstor
  2. sqlalchemy

Commits

Mike Bayer  committed 0143707

added 'post_update' attribute to PropertyLoader, means to defer processing of this property until after the object has been saved, and then to re-sync and force an update. used to break otherwise intra-row cycles. added for "many-to-one" so far.

  • Participants
  • Parent commits 48a44d8
  • Branches default

Comments (0)

Files changed (2)

File lib/sqlalchemy/mapping/mapper.py

View file
         self.columntoproperty[column.original][0].setattr(obj, value)
 
         
-    def save_obj(self, objects, uow):
+    def save_obj(self, objects, uow, postupdate=False):
         """called by a UnitOfWork object to save objects, which involves either an INSERT or
         an UPDATE statement for each table used by this mapper, for each element of the
         list."""
                 #print "SAVE_OBJ we are Mapper(" + str(id(self)) + ") obj: " +  obj.__class__.__name__ + repr(id(obj))
                 params = {}
 
-                isinsert = not hasattr(obj, "_instance_key")
+                # 'postupdate' means a PropertyLoader is telling us, "yes I know you 
+                # already inserted/updated this row but I need you to UPDATE one more 
+                # time"
+                isinsert = not postupdate and not hasattr(obj, "_instance_key")
                 if isinsert:
                     self.extension.before_insert(self, obj)
                 else:

File lib/sqlalchemy/mapping/properties.py

View file
 
     """describes an object property that holds a single item or list of items that correspond
     to a related database table."""
-    def __init__(self, argument, secondary, primaryjoin, secondaryjoin, foreignkey=None, uselist=None, private=False, association=None, use_alias=False, selectalias=None, order_by=False, attributeext=None, backref=None, is_backref=False):
+    def __init__(self, argument, secondary, primaryjoin, secondaryjoin, foreignkey=None, uselist=None, private=False, association=None, use_alias=False, selectalias=None, order_by=False, attributeext=None, backref=None, is_backref=False, post_update=False):
         self.uselist = uselist
         self.argument = argument
         self.secondary = secondary
         self.primaryjoin = primaryjoin
         self.secondaryjoin = secondaryjoin
+        self.post_update = post_update
         
         # a list of columns representing "the other side"
         # of the relationship
             uowcommit.register_processor(stub, self, self.parent, False)
             uowcommit.register_processor(stub, self, self.parent, True)
         elif self.direction == PropertyLoader.ONETOMANY:
+            if self.post_update:
+                raise "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)
         elif self.direction == PropertyLoader.MANYTOONE:
-            uowcommit.register_dependency(self.mapper, self.parent)
-            uowcommit.register_processor(self.mapper, self, self.parent, False)
+            if self.post_update:
+                stub = PropertyLoader.MapperStub(self.mapper)
+                uowcommit.register_dependency(self.mapper, stub)
+                uowcommit.register_processor(stub, self, self.parent, False)
+                uowcommit.register_processor(stub, self, self.parent, True)
+            else:
+                uowcommit.register_dependency(self.mapper, self.parent)
+                uowcommit.register_processor(self.mapper, self, self.parent, False)
         else:
             raise " no foreign key ?"
 
         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.
-            pass
+            if self.post_update:
+                for obj in deplist:
+                    self._synchronize(obj, None, None, True)
+                    task.mapper.save_obj([obj], uowcommit, 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
                         #print "DELETE ASSOC OBJ", repr(child)
                         uowcommit.register_object(child, isdelete=True)
         else:
-            for obj in deplist:
+            for obj in deplist:            
                 if self.direction == PropertyLoader.MANYTOONE:
                     uowcommit.register_object(obj)
                 childlist = getlist(obj, passive=True)
                 for child in childlist.added_items():
                     self._synchronize(obj, child, None, False)
                     if self.direction == PropertyLoader.ONETOMANY and child is not None:
-                        # for a cyclical task, this registration is handled by the objectstore
-                        uowcommit.register_object(child)
+                        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:
                     for child in childlist.deleted_items():
                         if not self.private:
                 value = None
             else:
                 value = self.source_mapper._getattrbycolumn(source, self.source_column)
-            #print "SYNC VALUE", value, "TO", dest
+            #print "SYNC VALUE", value, "TO", destination
             self.dest_mapper._setattrbycolumn(destination, self.dest_column, value)