[IMP] fields: update inverse fields lazily for performance
authorRaphael Collet <rco@openerp.com>
Tue, 2 Sep 2014 10:02:23 +0000 (12:02 +0200)
committerRaphael Collet <rco@openerp.com>
Tue, 2 Sep 2014 12:07:03 +0000 (14:07 +0200)
When a relational field is assigned in an onchange, its inverse field is
updated in cache.  Reading the current value of the inverse field may be
costly, for instance in the case of a one2many field with thousands of records
as a value.  Instead, put in cache a SpecialValue that reads and updates the
field; it will be triggered only when it is accessed.

openerp/fields.py

index a2dd7a0..d0055eb 100644 (file)
@@ -1386,13 +1386,32 @@ class Many2one(_Relational):
                 record[self.name] = record.env[self.comodel_name].new()
 
 
+class UnionUpdate(SpecialValue):
+    """ Placeholder for a value update; when this value is taken from the cache,
+        it returns ``record[field.name] | value`` and stores it in the cache.
+    """
+    def __init__(self, field, record, value):
+        self.args = (field, record, value)
+
+    def get(self):
+        field, record, value = self.args
+        # in order to read the current field's value, remove self from cache
+        del record._cache[field]
+        # read the current field's value, and update it in cache only
+        record._cache[field] = new_value = record[field.name] | value
+        return new_value
+
+
 class _RelationalMulti(_Relational):
     """ Abstract class for relational fields *2many. """
 
     def _update(self, records, value):
         """ Update the cached value of `self` for `records` with `value`. """
         for record in records:
-            record._cache[self] = record[self.name] | value
+            if self in record._cache:
+                record._cache[self] = record[self.name] | value
+            else:
+                record._cache[self] = UnionUpdate(self, record, value)
 
     def convert_to_cache(self, value, record, validate=True):
         if isinstance(value, BaseModel):