summaryrefslogtreecommitdiff
path: root/odoo/models.py
diff options
context:
space:
mode:
authorRaphael Collet <rco@odoo.com>2019-03-14 14:45:53 +0000
committerRaphael Collet <rco@odoo.com>2019-05-27 08:20:07 +0000
commitf14ae73a98ecb40c102c8a22729df4ceb44802e9 (patch)
tree4ee679d81e20d41d1af547602c2e5f3417272592 /odoo/models.py
parentec792da72b92b22349b6f325e53fd37d953c7db3 (diff)
[FIX] models: do less snapshots in onchange()
As snapshots are costly to compute, we now create exactly two snapshots: one initial snapshot and one final snapshot. All intermediate checks are done with respect to the initial snapshot. Note also that checks are costly for x2many fields. Therefore, once a field has changed, it will never be checked again. This optimization is correct since, by contract, the onchanges of a field are processed at most once. This guarantees the absence of cycles in onchange. For instance, assume that field A modifies fields B and C, and that field C modifies fields D, E and F: - before: 7 snapshots (snapshot, process A, snapshot, process B, snapshot, process C, snapshot, process D, snapshot, process E, snapshot, process F, snapshot) - after: 2 snapshots (snapshot, process A, process B, process C, process D, process E, process F, snapshot) closes odoo/odoo#31872 Signed-off-by: Raphael Collet (rco) <rco@openerp.com>
Diffstat (limited to 'odoo/models.py')
-rw-r--r--odoo/models.py46
1 files changed, 29 insertions, 17 deletions
diff --git a/odoo/models.py b/odoo/models.py
index bedf7fec10e..ab958f62644 100644
--- a/odoo/models.py
+++ b/odoo/models.py
@@ -5105,6 +5105,19 @@ class BaseModel(MetaModel('DummyModel', (object,), {'_register': False})):
if subnames else record[name]
)
+ def has_changed(self, name):
+ """ Return whether a field on record has changed. """
+ record = self['<record>']
+ subnames = self['<tree>'][name]
+ if not subnames:
+ return self[name] != record[name]
+ else:
+ return len(self[name]) != len(record[name]) or any(
+ line_snapshot.has_changed(subname)
+ for line_snapshot in self[name]
+ for subname in subnames
+ )
+
def diff(self, other):
""" Return the values in ``self`` that differ from ``other``.
Requires record cache invalidation for correct output!
@@ -5182,25 +5195,24 @@ class BaseModel(MetaModel('DummyModel', (object,), {'_register': False})):
result = {}
- # process names in order (or the keys of values if no name given)
- while todo:
- name = todo.pop(0)
- if name in done:
- continue
- done.add(name)
-
- with env.do_in_onchange():
+ # process names in order
+ with env.do_in_onchange():
+ while todo:
# apply field-specific onchange methods
- if field_onchange.get(name):
- record._onchange_eval(name, field_onchange[name], result)
-
- # make a snapshot (this forces evaluation of computed fields)
- snapshot1 = Snapshot(record, nametree)
+ for name in todo:
+ if field_onchange.get(name):
+ record._onchange_eval(name, field_onchange[name], result)
+ done.add(name)
+
+ # determine which fields to process for the next pass
+ todo = [
+ name
+ for name in nametree
+ if name not in done and snapshot0.has_changed(name)
+ ]
- # determine which fields have been modified
- for name in nametree:
- if snapshot1[name] != snapshot0[name]:
- todo.append(name)
+ # make the snapshot with the final values of record
+ snapshot1 = Snapshot(record, nametree)
# determine values that have changed by comparing snapshots
self.invalidate_cache()