summaryrefslogtreecommitdiff
path: root/odoo
diff options
context:
space:
mode:
authorRaphael Collet <rco@odoo.com>2018-01-04 15:22:17 +0100
committerRaphael Collet <rco@odoo.com>2018-01-12 13:42:05 +0100
commit3fbd86bcbe6dffe923e10d4690ee302c6a779f90 (patch)
tree903b69236072bc34212c83c92a2f125ec0a4a986 /odoo
parent9f4c1646bc64f870082f22abd9ba6b2b65a2945a (diff)
[IMP] field: compute transitive dependencies over non-stored fields
In other words, when a field F depends on a non-stored field G, it also depends on G's dependencies. This guarantees that whenever a dependency of G is modified, F will be invalidated and marked to recompute (if necessary). The transitive closure of dependencies is not computed over stored fields. Anyway stored fields already trigger the recomputation of their dependent fields during their recomputation. The performance impact on the loading of a registry is negligible (less than 1%), and the increase of recomputation triggers is small (less than 10%).
Diffstat (limited to 'odoo')
-rw-r--r--odoo/addons/test_new_api/ir.model.access.csv1
-rw-r--r--odoo/addons/test_new_api/models.py18
-rw-r--r--odoo/addons/test_new_api/tests/test_new_fields.py5
-rw-r--r--odoo/fields.py12
4 files changed, 32 insertions, 4 deletions
diff --git a/odoo/addons/test_new_api/ir.model.access.csv b/odoo/addons/test_new_api/ir.model.access.csv
index 4db53199c26..a88e202a86d 100644
--- a/odoo/addons/test_new_api/ir.model.access.csv
+++ b/odoo/addons/test_new_api/ir.model.access.csv
@@ -18,3 +18,4 @@ access_test_new_api_company,access_test_new_api_company,model_test_new_api_compa
access_test_new_api_company_attr,access_test_new_api_company_attr,model_test_new_api_company_attr,,1,1,1,1
access_test_new_api_compute_inverse,access_test_new_api_compute_inverse,model_test_new_api_compute_inverse,,1,1,1,1
access_test_new_api_recursive,access_test_new_api_recursive,model_test_new_api_recursive,,1,1,1,1
+access_test_new_api_cascade,access_test_new_api_cascade,model_test_new_api_cascade,,1,1,1,1
diff --git a/odoo/addons/test_new_api/models.py b/odoo/addons/test_new_api/models.py
index e6673e4f776..7298bfbe01a 100644
--- a/odoo/addons/test_new_api/models.py
+++ b/odoo/addons/test_new_api/models.py
@@ -373,3 +373,21 @@ class ComputeRecursive(models.Model):
rec.display_name = rec.parent.display_name + " / " + rec.name
else:
rec.display_name = rec.name
+
+
+class ComputeCascade(models.Model):
+ _name = 'test_new_api.cascade'
+
+ foo = fields.Char()
+ bar = fields.Char(compute='_compute_bar') # depends on foo
+ baz = fields.Char(compute='_compute_baz', store=True) # depends on bar
+
+ @api.depends('foo')
+ def _compute_bar(self):
+ for record in self:
+ record.bar = "[%s]" % (record.foo or "")
+
+ @api.depends('bar')
+ def _compute_baz(self):
+ for record in self:
+ record.baz = "<%s>" % (record.bar or "")
diff --git a/odoo/addons/test_new_api/tests/test_new_fields.py b/odoo/addons/test_new_api/tests/test_new_fields.py
index a486a99f606..f8e60e23b6b 100644
--- a/odoo/addons/test_new_api/tests/test_new_fields.py
+++ b/odoo/addons/test_new_api/tests/test_new_fields.py
@@ -214,6 +214,11 @@ class TestFields(common.TransactionCase):
double_size = message.double_size
self.assertEqual(double_size, message.size)
+ record = self.env['test_new_api.cascade'].create({'foo': "Hi"})
+ self.assertEqual(record.baz, "<[Hi]>")
+ record.foo = "Ho"
+ self.assertEqual(record.baz, "<[Ho]>")
+
def test_13_inverse(self):
""" test inverse computation of fields """
Category = self.env['test_new_api.category']
diff --git a/odoo/fields.py b/odoo/fields.py
index b6b13e97c8c..2e3d4d7eed2 100644
--- a/odoo/fields.py
+++ b/odoo/fields.py
@@ -658,7 +658,7 @@ class Field(MetaField('DummyField', (object,), {})):
# on ``path``. See method ``modified`` below for details.
#
- def resolve_deps(self, model):
+ def resolve_deps(self, model, path0=[], seen=frozenset()):
""" Return the dependencies of ``self`` as tuples ``(model, field, path)``,
where ``path`` is an optional list of field names.
"""
@@ -669,11 +669,12 @@ class Field(MetaField('DummyField', (object,), {})):
for dotnames in self.depends:
if dotnames == self.name:
_logger.warning("Field %s depends on itself; please fix its decorator @api.depends().", self)
- model, path = model0, dotnames.split('.')
- for i, fname in enumerate(path):
+ model, path = model0, path0
+ for fname in dotnames.split('.'):
field = model._fields[fname]
- result.append((model, field, path[:i]))
+ result.append((model, field, path))
model = model0.env.get(field.comodel_name)
+ path = None if path is None else path + [fname]
# add self's model dependencies
for mname, fnames in model0._depends.items():
@@ -683,11 +684,14 @@ class Field(MetaField('DummyField', (object,), {})):
result.append((model, field, None))
# add indirect dependencies from the dependencies found above
+ seen = seen.union([self])
for model, field, path in list(result):
for inv_field in model._field_inverses[field]:
inv_model = model0.env[inv_field.model_name]
inv_path = None if path is None else path + [field.name]
result.append((inv_model, inv_field, inv_path))
+ if not field.store and field not in seen:
+ result += field.resolve_deps(model, path, seen)
return result