[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.
This commit is contained in:
Raphael Collet
2018-01-12 14:13:37 +01:00
parent 3fbd86bcbe
commit ca9d2b3006
+19 -8
View File
@@ -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))