[IMP] core: compute field_computed lazily

This is a simple refactoring.

X-original-commit: 634775bff6d9eba9d4548cc801071a942895a719
This commit is contained in:
Raphael Collet
2020-04-03 11:40:28 +00:00
parent 0551e7ad54
commit 892e3df701
6 changed files with 25 additions and 22 deletions
@@ -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
+1 -1
View File
@@ -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:
+1 -1
View File
@@ -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)
+2 -2
View File
@@ -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
+4 -17
View File
@@ -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)])
+16
View File
@@ -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