summaryrefslogtreecommitdiff
path: root/odoo/fields.py
diff options
context:
space:
mode:
authorRaphael Collet <rco@odoo.com>2019-08-27 09:51:04 +0000
committerRaphael Collet <rco@odoo.com>2019-09-09 13:18:48 +0000
commitcfb08e87b0fa8895ec4406c9b30d2ebdb790e346 (patch)
tree4ac1ff2d8a34e3fb215dfb6b562f287fea71bf34 /odoo/fields.py
parent3d2df88ffd5cfb9644bc54744a6c9b9a7fcfc264 (diff)
[FIX] models: use transitive triggers instead of recursion
On average, this reduces the total time spent in method `modified` by half (times measured on invoice creation and post).
Diffstat (limited to 'odoo/fields.py')
-rw-r--r--odoo/fields.py61
1 files changed, 15 insertions, 46 deletions
diff --git a/odoo/fields.py b/odoo/fields.py
index d658ad5782c..995a586739f 100644
--- a/odoo/fields.py
+++ b/odoo/fields.py
@@ -692,64 +692,33 @@ class Field(MetaField('DummyField', (object,), {})):
#
# Setup of field triggers
#
- # The triggers of a field F is a tree that contains the fields that depend
- # on F, together with the fields to inverse to find out which records to
- # recompute.
- #
- # For instance, assume that G depends on F, H depends on X.F, I depends on
- # W.X.F, and J depends on Y.F. The triggers of F will be the tree:
- #
- # [G]
- # X/ \Y
- # [H] [J]
- # W/
- # [I]
- #
- # This tree provides perfect support for the trigger mechanism:
- # when F is # modified on records,
- # - mark G to recompute on records,
- # - mark H to recompute on inverse(X, records),
- # - mark I to recompute on inverse(W, inverse(X, records)),
- # - mark J to recompute on inverse(Y, records).
-
- def setup_triggers(self, model):
- def add_trigger(field, path):
- """ add a trigger on field to recompute self """
- field_model = model.env[field.model_name]
- # trigger computations depending on one2many fields only at creation
- nodes = [field_model._field_triggers_create.setdefault(field, {})]
- if (field.type != 'one2many') or not field_model._field_inverses[field]:
- nodes.append(field_model._field_triggers.setdefault(field, {}))
- for node in nodes:
- for f in reversed(path):
- node = node.setdefault(f, {})
- n = node.setdefault(None, [])
- if self not in n: n.append(self)
+ def resolve_depends(self, model):
+ """ Return the dependencies of `self` as a collection of field tuples. """
for dotnames in self.depends:
+ field_seq = []
field_model = model
- path = [] # fields from model to field_model
- for fname in dotnames.split('.'):
- field = field_model._fields[fname]
-
+ for index, fname in enumerate(dotnames.split('.')):
if model._transient and not field_model._transient:
# modifying fields on regular models should not trigger
# recomputations of fields on transient models
break
- # Do not make self trigger itself
- # e.g. `fields.One2many('stock.move.line', 'move_id', domain=[('product_qty', '=', 0.0)])`
- # will have 'move_line_nosuggest_ids.product_qty' as a dependency
- if (field is not self) or path:
- add_trigger(field, path)
-
- if (field is self) and path:
+ field = field_model._fields[fname]
+ if field is self and index:
self.recursive = True
- path.append(field)
+ field_seq.append(field)
+
+ # do not make self trigger itself: for instance, a one2many
+ # field line_ids with domain [('foo', ...)] will have
+ # 'line_ids.foo' as a dependency
+ if not (field is self and not index):
+ yield tuple(field_seq)
+
if field.type in ('one2many', 'many2many'):
for inv_field in field_model._field_inverses[field]:
- add_trigger(inv_field, path)
+ yield tuple(field_seq) + (inv_field,)
field_model = model.env.get(field.comodel_name)