From 892e3df7010729da96278becbec29d7fe1f0985d Mon Sep 17 00:00:00 2001 From: Raphael Collet Date: Thu, 2 Apr 2020 06:45:54 +0000 Subject: [PATCH] [IMP] core: compute field_computed lazily This is a simple refactoring. X-original-commit: 634775bff6d9eba9d4548cc801071a942895a719 --- .../base_automation/models/base_automation.py | 2 +- addons/event/models/event_event.py | 2 +- addons/mail/models/mail_thread.py | 2 +- odoo/fields.py | 4 ++-- odoo/models.py | 21 ++++--------------- odoo/modules/registry.py | 16 ++++++++++++++ 6 files changed, 25 insertions(+), 22 deletions(-) diff --git a/addons/base_automation/models/base_automation.py b/addons/base_automation/models/base_automation.py index 3ac633948c0..5051ed9f60d 100644 --- a/addons/base_automation/models/base_automation.py +++ b/addons/base_automation/models/base_automation.py @@ -343,7 +343,7 @@ class BaseAutomation(models.Model): # def _compute_field_value(self, field): # determine fields that may trigger an action - stored_fields = [f for f in self._field_computed[field] if f.store] + stored_fields = [f for f in self.pool.field_computed[field] if f.store] if not any(stored_fields): return _compute_field_value.origin(self, field) # retrieve the action rules to possibly execute diff --git a/addons/event/models/event_event.py b/addons/event/models/event_event.py index 00ca45c62a7..de6edbef4fb 100644 --- a/addons/event/models/event_event.py +++ b/addons/event/models/event_event.py @@ -342,7 +342,7 @@ class EventEvent(models.Model): """ Make it separate from ``_compute_from_event_type`` because otherwise a value given at create (see create override) would protect all other fields depending on event type id from being computed as compute method will be - blacklisted during create (see ``_field_computed`` attribute used in create + blacklisted during create (see ``registry.field_computed`` attribute used in create to compute protected field from re-computation) """ for event in self: if event.event_type_id.seats_max: diff --git a/addons/mail/models/mail_thread.py b/addons/mail/models/mail_thread.py index ff31ce7deb6..8b639ae5207 100644 --- a/addons/mail/models/mail_thread.py +++ b/addons/mail/models/mail_thread.py @@ -324,7 +324,7 @@ class MailThread(models.AbstractModel): def _compute_field_value(self, field): if not self._context.get('tracking_disable') and not self._context.get('mail_notrack'): - self._prepare_tracking(f.name for f in self._field_computed[field] if f.store) + self._prepare_tracking(f.name for f in self.pool.field_computed[field] if f.store) return super()._compute_field_value(field) diff --git a/odoo/fields.py b/odoo/fields.py index 2f4764f6233..11ba2836853 100644 --- a/odoo/fields.py +++ b/odoo/fields.py @@ -1044,7 +1044,7 @@ class Field(MetaField('DummyField', (object,), {})): if new_ids: # new records: no business logic new_records = records.browse(new_ids) - with records.env.protecting(records._field_computed.get(self, [self]), records): + with records.env.protecting(records.pool.field_computed.get(self, [self]), records): new_records.modified([self.name]) self.write(new_records, value) if self.relational: @@ -1071,7 +1071,7 @@ class Field(MetaField('DummyField', (object,), {})): env = records.env if self.compute_sudo: records = records.sudo() - fields = records._field_computed[self] + fields = records.pool.field_computed[self] # Just in case the compute method does not assign a value, we already # mark the computation as done. This is also necessary if the compute diff --git a/odoo/models.py b/odoo/models.py index 9b8bd13d507..1c0ecbc20ee 100644 --- a/odoo/models.py +++ b/odoo/models.py @@ -2707,7 +2707,6 @@ class BaseModel(MetaModel('DummyModel', (object,), {'_register': False})): self._add_inherited_fields() # 4. initialize more field metadata - cls._field_computed = {} # fields computed with the same method cls._field_inverses = Collector() # inverse fields for related fields cls._setup_done = True @@ -2763,18 +2762,6 @@ class BaseModel(MetaModel('DummyModel', (object,), {'_register': False})): field = cls._fields['display_name'] field.depends = tuple(name for name in field.depends if name != 'x_name') - # map each field to the fields computed with the same method - groups = defaultdict(list) - for field in cls._fields.values(): - if field.compute: - cls._field_computed[field] = group = groups[field.compute] - group.append(field) - for fields in groups.values(): - compute_sudo = fields[0].compute_sudo - if not all(field.compute_sudo == compute_sudo for field in fields): - _logger.warning("%s: inconsistent 'compute_sudo' for computed fields: %s", - self._name, ", ".join(field.name for field in fields)) - @api.model def _setup_complete(self): """ Setup recomputation triggers, and complete the model setup. """ @@ -3546,7 +3533,7 @@ Record ids: %(records)s # will automatically invalidate the field from the cache, # forcing its value to be recomputed once dependencies are # up-to-date. - protected.update(self._field_computed.get(field, [field])) + protected.update(self.pool.field_computed.get(field, [field])) if fname == 'company_id' or (field.relational and field.check_company): check_company = True @@ -3753,7 +3740,7 @@ Record ids: %(records)s inversed_fields.add(field) # protect non-readonly computed fields against (re)computation if field.compute and not field.readonly: - protected.update(self._field_computed.get(field, [field])) + protected.update(self.pool.field_computed.get(field, [field])) data_list.append(data) @@ -3955,7 +3942,7 @@ Record ids: %(records)s if field.store and any(self._ids): # check constraints of the fields that have been computed - fnames = [f.name for f in self._field_computed[field]] + fnames = [f.name for f in self.pool.field_computed[field]] self.filtered('id')._validate_fields(fnames) def _parent_store_create(self): @@ -5828,7 +5815,7 @@ Record ids: %(records)s # mark the field as computed on missing records, otherwise # they remain forever in the todo list, and lead to an # infinite loop... - for f in recs._field_computed[field]: + for f in recs.pool.field_computed[field]: self.env.remove_to_compute(f, recs - existing) else: self.env.cache.invalidate([(field, recs._ids)]) diff --git a/odoo/modules/registry.py b/odoo/modules/registry.py index ec07606d6f9..843cc14d97c 100644 --- a/odoo/modules/registry.py +++ b/odoo/modules/registry.py @@ -266,6 +266,22 @@ class Registry(Mapping): self.registry_invalidated = True + @lazy_property + def field_computed(self): + """ Return a dict mapping each field to the fields computed by the same method. """ + computed = {} + for Model in self.models.values(): + groups = defaultdict(list) + for field in Model._fields.values(): + if field.compute: + computed[field] = group = groups[field.compute] + group.append(field) + for fields in groups.values(): + if len({field.compute_sudo for field in fields}) > 1: + _logger.warning("%s: inconsistent 'compute_sudo' for computed fields: %s", + self._name, ", ".join(field.name for field in fields)) + return computed + @lazy_property def field_triggers(self): # determine field dependencies