[FIX] models: do not prefetch fields to recompute, and recompute once only
authorRaphael Collet <rco@openerp.com>
Mon, 13 Oct 2014 10:38:59 +0000 (12:38 +0200)
committerRaphael Collet <rco@openerp.com>
Mon, 13 Oct 2014 10:38:59 +0000 (12:38 +0200)
The method _prefetch_field() was accidentally prefetching fields to recompute;
which was skipping the actual recomputation, since a value was put in cache.
But sometimes the field's value was fixed by an extra recomputation of the
field.  Here we remove the extra recomputation and fix the cache corruption.

openerp/api.py
openerp/models.py

index a2385a8..da9e62f 100644 (file)
@@ -837,11 +837,10 @@ class Environment(object):
 
     def remove_todo(self, field, records):
         """ Mark `field` as recomputed on `records`. """
-        recs_list = self.all.todo.get(field, [])
-        if records in recs_list:
-            recs_list.remove(records)
-            if not recs_list:
-                del self.all.todo[field]
+        recs_list = [recs - records for recs in self.all.todo.pop(field, [])]
+        recs_list = filter(None, recs_list)
+        if recs_list:
+            self.all.todo[field] = recs_list
 
     def has_todo(self):
         """ Return whether some fields must be recomputed. """
index 2dcf464..1457a4b 100644 (file)
@@ -3127,25 +3127,27 @@ class BaseModel(object):
         if len(records) > PREFETCH_MAX:
             records = records[:PREFETCH_MAX] | self
 
-        # by default, simply fetch field
-        fnames = {field.name}
-
-        if self.env.in_draft:
-            # we may be doing an onchange, do not prefetch other fields
-            pass
-        elif self.env.field_todo(field):
-            # field must be recomputed, do not prefetch records to recompute
-            records -= self.env.field_todo(field)
-        elif not self._context.get('prefetch_fields', True):
-            # do not prefetch other fields
-            pass
-        elif self._columns[field.name]._prefetch:
-            # here we can optimize: prefetch all classic and many2one fields
-            fnames = set(fname
+        # determine which fields can be prefetched
+        if not self.env.in_draft and \
+                self._context.get('prefetch_fields', True) and \
+                self._columns[field.name]._prefetch:
+            # prefetch all classic and many2one fields that the user can access
+            fnames = {fname
                 for fname, fcolumn in self._columns.iteritems()
                 if fcolumn._prefetch
                 if not fcolumn.groups or self.user_has_groups(fcolumn.groups)
-            )
+            }
+        else:
+            fnames = {field.name}
+
+        # important: never prefetch fields to recompute!
+        get_recs_todo = self.env.field_todo
+        for fname in list(fnames):
+            if get_recs_todo(self._fields[fname]):
+                if fname == field.name:
+                    records -= get_recs_todo(field)
+                else:
+                    fnames.discard(fname)
 
         # fetch records with read()
         assert self in records and field.name in fnames