From ca9d2b30062a6d366f568ec96f3cca095882026b Mon Sep 17 00:00:00 2001 From: Raphael Collet Date: Fri, 12 Jan 2018 14:07:31 +0100 Subject: [PATCH] [IMP] fields: log when a stored field depends on a non-stored field Log an info when a field is computed and stored, but not all its dependencies are stored. Also allow to set explicitly `depends` on a field; this is useful to restrict the dependencies of a related field. --- odoo/fields.py | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/odoo/fields.py b/odoo/fields.py index 2e3d4d7eed2..d96f26113cd 100644 --- a/odoo/fields.py +++ b/odoo/fields.py @@ -297,7 +297,7 @@ class Field(MetaField('DummyField', (object,), {})): 'index': False, # whether the field is indexed in database 'manual': False, # whether the field is a custom field 'copy': True, # whether the field is copied over by BaseModel.copy() - 'depends': (), # collection of field dependencies + 'depends': None, # collection of field dependencies 'recursive': False, # whether self depends on itself 'compute': None, # compute(recs) computes field on recs 'compute_sudo': False, # whether field should be recomputed as admin @@ -437,6 +437,8 @@ class Field(MetaField('DummyField', (object,), {})): if attrs.get('translate'): # by default, translatable fields are context-dependent attrs['context_dependent'] = attrs.get('context_dependent', True) + if 'depends' in attrs: + attrs['depends'] = tuple(attrs['depends']) return attrs @@ -487,16 +489,22 @@ class Field(MetaField('DummyField', (object,), {})): def _setup_regular_base(self, model): """ Setup the attributes of a non-related field. """ - def make_depends(deps): - return tuple(deps(model) if callable(deps) else deps) + if self.depends is not None: + return + + def get_depends(func): + deps = getattr(func, '_depends', ()) + return deps(model) if callable(deps) else deps if isinstance(self.compute, pycompat.string_types): # if the compute method has been overridden, concatenate all their _depends - self.depends = () - for method in resolve_mro(model, self.compute, callable): - self.depends += make_depends(getattr(method, '_depends', ())) + self.depends = tuple( + dep + for method in resolve_mro(model, self.compute, callable) + for dep in get_depends(method) + ) else: - self.depends = make_depends(getattr(self.compute, '_depends', ())) + self.depends = tuple(get_depends(self.compute)) def _setup_regular_full(self, model): """ Setup the inverse field(s) of ``self``. """ @@ -526,7 +534,8 @@ class Field(MetaField('DummyField', (object,), {})): raise TypeError("Type of related field %s is inconsistent with %s" % (self, field)) # determine dependencies, compute, inverse, and search - self.depends = ('.'.join(self.related),) + if self.depends is None: + self.depends = ('.'.join(self.related),) self.compute = self._compute_related if not (self.readonly or field.readonly): self.inverse = self._inverse_related @@ -698,6 +707,8 @@ class Field(MetaField('DummyField', (object,), {})): def setup_triggers(self, model): """ Add the necessary triggers to invalidate/recompute ``self``. """ for model, field, path in self.resolve_deps(model): + if self.store and not field.store: + _logger.info("Field %s depends on non-stored field %s", self, field) if field is not self: path_str = None if path is None else ('.'.join(path) or 'id') model._field_triggers.add(field, (self, path_str))