diff options
| author | Raphael Collet <rco@odoo.com> | 2017-07-06 17:16:03 +0200 |
|---|---|---|
| committer | Raphael Collet <rco@odoo.com> | 2017-07-10 16:18:33 +0200 |
| commit | e25bcf41e2f312536a00ef8531b2f6c6e0334ebc (patch) | |
| tree | cd4fed7e6ea124b024637c5327f0e73bce376148 /odoo/models.py | |
| parent | c47a41b8b4c568ec63692a027c4dc13c82f1f172 (diff) | |
[REF] models: group recomputation triggers to minimize the number of requests
When several fields are modified and trigger the same recomputations, the same
SQL queries were made to determine the records to recompute, because each field
was processed independently. In order to avoid duplicated SQL queries, process
the triggers of the modified fields altogether.
This reduces the number of queries by 15% to 25%, depending on the number of
computed fields.
Diffstat (limited to 'odoo/models.py')
| -rw-r--r-- | odoo/models.py | 46 |
1 files changed, 41 insertions, 5 deletions
diff --git a/odoo/models.py b/odoo/models.py index 2222555fd00..ca40b271748 100644 --- a/odoo/models.py +++ b/odoo/models.py @@ -4713,12 +4713,48 @@ class BaseModel(MetaModel('DummyModel', (object,), {'_register': False})): :param fnames: iterable of field names that have been modified on records ``self`` """ - # each field knows what to invalidate and recompute - spec = [] + # group triggers by (model, path) to minimize the calls to search() + invalids = [] + triggers = defaultdict(set) for fname in fnames: - spec += self._fields[fname].modified(self) - - self.env.invalidate(spec) + mfield = self._fields[fname] + # invalidate mfield on self, and its inverses fields + invalids.append((mfield, self._ids)) + for field in self._field_inverses[mfield]: + invalids.append((field, None)) + # group triggers by model and path to reduce the number of search() + for field, path in self._field_triggers[mfield]: + triggers[(field.model_name, path)].add(field) + + # process triggers, mark fields to be invalidated/recomputed + for model_path, fields in triggers.items(): + model_name, path = model_path + stored = {field for field in fields if field.compute and field.store} + # process stored fields + if path and stored: + # determine records of model_name linked by path to self + if path == 'id': + target0 = self + else: + env = self.env(user=SUPERUSER_ID, context={'active_test': False}) + target0 = env[model_name].search([(path, 'in', self.ids)]) + target0 = target0.with_env(self.env) + # prepare recomputation for each field on linked records + for field in stored: + # discard records to not recompute for field + target = target0 - self.env.protected(field) + if not target: + continue + invalids.append((field, target._ids)) + # mark field to be recomputed on target + if field.compute_sudo: + target = target.sudo() + target._recompute_todo(field) + # process non-stored fields + for field in (fields - stored): + invalids.append((field, None)) + + self.env.invalidate(invalids) def _recompute_check(self, field): """ If ``field`` must be recomputed on some record in ``self``, return the |
