]> git.ipfire.org Git - thirdparty/sqlalchemy/sqlalchemy.git/commitdiff
mapper, when updating, only SET's those columns that have changed.
authorMike Bayer <mike_mp@zzzcomputing.com>
Fri, 23 Dec 2005 05:19:48 +0000 (05:19 +0000)
committerMike Bayer <mike_mp@zzzcomputing.com>
Fri, 23 Dec 2005 05:19:48 +0000 (05:19 +0000)
this also allows "deferred" column properties to remain untouched by a save operation
if they werent affected.

lib/sqlalchemy/mapping/mapper.py
lib/sqlalchemy/mapping/properties.py
test/mapper.py
test/objectstore.py

index 9f7cc7177cdef50c783e305024fc448b1488c9e9..df308a1a22dd46fa56894056731a89fc78dfe036 100644 (file)
@@ -413,7 +413,7 @@ class Mapper(object):
         t = sql.text(text, engine=self.primarytable.engine)
         return self.instances(t.execute(**params))
 
-    def _getattrbycolumn(self, obj, column):
+    def _getpropbycolumn(self, column):
         try:
             prop = self.columntoproperty[column.original]
         except KeyError:
@@ -422,8 +422,11 @@ class Mapper(object):
                 raise "Column '%s.%s' is not available, due to conflicting property '%s':%s" % (column.table.name, column.name, column.key, repr(prop))
             except KeyError:
                 raise "No column %s.%s is configured on mapper %s..." % (column.table.name, column.name, str(self))
-                
-        return prop[0].getattr(obj)
+        return prop[0]
+        
+    def _getattrbycolumn(self, obj, column):
+        prop = self._getpropbycolumn(column)
+        return prop.getattr(obj)
 
     def _setattrbycolumn(self, obj, column, value):
         self.columntoproperty[column.original][0].setattr(obj, value)
@@ -453,9 +456,11 @@ class Mapper(object):
 #                print "SAVE_OBJ we are " + hash_key(self) + " obj: " +  obj.__class__.__name__ + repr(id(obj))
                 params = {}
 
-                if not hasattr(obj, "_instance_key"):
+                isinsert = not hasattr(obj, "_instance_key")
+                if isinsert:
                     self.extension.before_insert(self, obj)
 
+                hasdata = False
                 for col in table.columns:
                     #if col.primary_key:
                     if pk.has_key(col):
@@ -468,10 +473,20 @@ class Mapper(object):
                             if value is not None:
                                 params[col.key] = value
                     else:
-                        params[col.key] = self._getattrbycolumn(obj, col)
+                        if not isinsert:
+                            prop = self._getpropbycolumn(col)
+                            history = prop.get_history(obj, passive=True)
+                            if history:
+                                a = history.added_items()
+                                if len(a):
+                                    params[col.key] = a[0]
+                                    hasdata = True
+                        else:
+                            params[col.key] = self._getattrbycolumn(obj, col)
 
-                if hasattr(obj, "_instance_key"):
-                    update.append(params)
+                if not isinsert:
+                    if hasdata:
+                        update.append(params)
                 else:
                     insert.append((obj, params))
                 uow.register_saved_object(obj)
@@ -481,9 +496,12 @@ class Mapper(object):
                 for col in self.pks_by_table[table]:
                     clause.clauses.append(col == sql.bindparam(col.table.name + "_" + col.key))
                 statement = table.update(clause)
-                c = statement.execute(*update)
-                if table.engine.supports_sane_rowcount() and c.rowcount != len(update):
-                    raise "ConcurrencyError - updated rowcount %d does not match number of objects updated %d" % (c.cursor.rowcount, len(update))
+                rows = 0
+                for rec in update:
+                    c = statement.execute(rec)
+                    rows += c.cursor.rowcount
+                if table.engine.supports_sane_rowcount() and rows != len(update):
+                    raise "ConcurrencyError - updated rowcount %d does not match number of objects updated %d" % (rows, len(update))
             if len(insert):
                 import sys
                 statement = table.insert()
index b7b59508ef0c2b034b473099ea00b17159a333a3..fc2460e4ab4932bfee1942e8b453bd78a80f48d8 100644 (file)
@@ -38,6 +38,8 @@ class ColumnProperty(MapperProperty):
         return getattr(object, self.key, None)
     def setattr(self, object, value):
         setattr(object, self.key, value)
+    def get_history(self, obj, passive=False):
+        return objectstore.global_attributes.get_history(obj, self.key, passive=passive)
     def hash_key(self):
         return "ColumnProperty(%s)" % repr([hash_key(c) for c in self.columns])
 
index fca87518ce107433a3143522f07ce51c35d07df0..d07a6488b9a5fb32b64943de3501f9c53be7ff75 100644 (file)
@@ -215,9 +215,19 @@ class DeferredTest(MapperSuperTest):
             print o2.description
 
         self.assert_sql(db, go, [
-            ("SELECT orders.order_id AS orders_order_id, orders.user_id AS orders_user_id, orders.isopen AS orders_isopen FROM orders ORDER BY orders.oid", {}),
+            ("SELECT orders.order_id AS orders_order_id, orders.user_id AS orders_user_id, orders.isopen AS orders_isopen FROM orders ORDER BY orders.%s" % orders.rowid_column.key, {}),
             ("SELECT orders.description AS orders_description FROM orders WHERE orders.order_id = :orders_order_id", {'orders_order_id':3})
         ])
+    
+    def testsave(self):
+        m = mapper(Order, orders, properties={
+            'description':deferred(orders.c.description)
+        })
+        
+        l = m.select()
+        o2 = l[2]
+        o2.isopen = 1
+        objectstore.commit()
         
     def testgroup(self):
         """tests deferred load with a group"""
@@ -233,7 +243,7 @@ class DeferredTest(MapperSuperTest):
             o2 = l[2]
             print o2.opened, o2.description, o2.userident
         self.assert_sql(db, go, [
-            ("SELECT orders.order_id AS orders_order_id FROM orders ORDER BY orders.oid", {}),
+            ("SELECT orders.order_id AS orders_order_id FROM orders ORDER BY orders.%s" % orders.rowid_column.key, {}),
             ("SELECT orders.user_id AS orders_user_id, orders.description AS orders_description, orders.isopen AS orders_isopen FROM orders WHERE orders.order_id = :orders_order_id", {'orders_order_id':3})
         ])
         
@@ -245,7 +255,7 @@ class DeferredTest(MapperSuperTest):
             l = m2.select()
             print l[2].user_id
         self.assert_sql(db, go, [
-            ("SELECT orders.order_id AS orders_order_id, orders.description AS orders_description, orders.isopen AS orders_isopen FROM orders ORDER BY orders.oid", {}),
+            ("SELECT orders.order_id AS orders_order_id, orders.description AS orders_description, orders.isopen AS orders_isopen FROM orders ORDER BY orders.%s" % orders.rowid_column.key, {}),
             ("SELECT orders.user_id AS orders_user_id FROM orders WHERE orders.order_id = :orders_order_id", {'orders_order_id':3})
         ])
         objectstore.clear()
@@ -255,7 +265,7 @@ class DeferredTest(MapperSuperTest):
             l = m3.select()
             print l[3].user_id
         self.assert_sql(db, go, [
-            ("SELECT orders.order_id AS orders_order_id, orders.user_id AS orders_user_id, orders.description AS orders_description, orders.isopen AS orders_isopen FROM orders ORDER BY orders.oid", {}),
+            ("SELECT orders.order_id AS orders_order_id, orders.user_id AS orders_user_id, orders.description AS orders_description, orders.isopen AS orders_isopen FROM orders ORDER BY orders.%s" % orders.rowid_column.key, {}),
         ])
 
     def testdeepoptions(self):
index 9fbce163720847e5b4672db7df740301c2084e15..9da6437d3f68cd621426fa6aaf259b454f023bf6 100644 (file)
@@ -350,12 +350,14 @@ class SaveTest(AssertMixin):
                     {'user_name': 'imnewlyadded'}
                 ),
                 (
-                    "UPDATE email_addresses SET user_id=:user_id, email_address=:email_address WHERE email_addresses.address_id = :email_addresses_address_id",
-                    lambda: [
-                        {'email_address': 'imnew@foo.bar', 'user_id': objects[2].user.user_id, 'email_addresses_address_id': objects[2].address_id}, 
-                        {'email_address': 'adsd5@llala.net', 'user_id': objects[3].user.user_id, 'email_addresses_address_id': objects[3].address_id}
-                    ]
-                )
+                    "UPDATE email_addresses SET email_address=:email_address WHERE email_addresses.address_id = :email_addresses_address_id",
+                    lambda: [{'email_address': 'imnew@foo.bar', 'email_addresses_address_id': objects[2].address_id}]
+                ),
+                (
+                    "UPDATE email_addresses SET user_id=:user_id WHERE email_addresses.address_id = :email_addresses_address_id",
+                    lambda: [{'user_id': objects[3].user.user_id, 'email_addresses_address_id': objects[3].address_id}]
+                ),
+                
         ])
         l = sql.select([users, addresses], sql.and_(users.c.user_id==addresses.c.address_id, addresses.c.address_id==a.address_id)).execute()
         self.echo( repr(l.fetchone().row))
@@ -469,11 +471,11 @@ class SaveTest(AssertMixin):
                         [{'users_user_id': u2.user_id, 'user_name': 'user2modified'}]
                     ),
                     (
-                        "UPDATE email_addresses SET user_id=:user_id, email_address=:email_address WHERE email_addresses.address_id = :email_addresses_address_id",
-                        [
-                            {'email_address': 'emailaddress3', 'user_id': u1.user_id, 'email_addresses_address_id': a3.address_id}, 
-                            {'email_address': 'emailaddress1', 'user_id': None, 'email_addresses_address_id': a1.address_id}
-                        ]
+                        "UPDATE email_addresses SET user_id=:user_id WHERE email_addresses.address_id = :email_addresses_address_id",
+                        [{'user_id': u1.user_id, 'email_addresses_address_id': a3.address_id}]
+                    ),
+                    ("UPDATE email_addresses SET user_id=:user_id WHERE email_addresses.address_id = :email_addresses_address_id",
+                        [{'user_id': None, 'email_addresses_address_id': a1.address_id}]
                     )
                 ])
 
@@ -582,8 +584,8 @@ class SaveTest(AssertMixin):
         objects[5].keywords.append(k)
         self.assert_sql(db, lambda:objectstore.commit(), [
             (
-                "UPDATE items SET order_id=:order_id, item_name=:item_name WHERE items.item_id = :items_item_id",
-                [{'item_name': 'item4updated', 'order_id': None, 'items_item_id': objects[4].item_id}]
+                "UPDATE items SET item_name=:item_name WHERE items.item_id = :items_item_id",
+                [{'item_name': 'item4updated', 'items_item_id': objects[4].item_id}]
             ),
             (
                 "INSERT INTO keywords (name) VALUES (:name)",