[FIX] core: marking of stored computed recursive fields during creation

Fixes 3a6ac95 that ignores computation of recursive stored fields during
creation of records.

The problem happens when we have:

* a recursive stored computed field
* an `api.depends` of this field which is a relation to another record

When this "another record" is created and should recompute the recursive
field, it is ignored.

closes odoo/odoo#64117

X-original-commit: 9fe4fe404b73e9ab18ab6b3b913f075c64bb6954
Signed-off-by: Raphael Collet (rco) <rco@openerp.com>
Co-authored-by: Raphael Collet (rco) <rco@openerp.com>
This commit is contained in:
Guewen Baconnier
2021-01-05 19:09:16 +00:00
committed by Raphael Collet
co-authored by Raphael Collet
parent c314cb4e98
commit e14b52a56d
4 changed files with 36 additions and 12 deletions
@@ -513,6 +513,22 @@ class ComputeRecursive(models.Model):
rec.display_name = rec.name
class ComputeRecursiveTree(models.Model):
_name = 'test_new_api.recursive.tree'
_description = 'Test New API Recursive with one2many field'
name = fields.Char(required=True)
parent_id = fields.Many2one('test_new_api.recursive.tree', ondelete='cascade')
children_ids = fields.One2many('test_new_api.recursive.tree', 'parent_id')
display_name = fields.Char(compute='_compute_display_name', store=True)
@api.depends('name', 'children_ids.display_name')
def _compute_display_name(self):
for rec in self:
children_names = rec.mapped('children_ids.display_name')
rec.display_name = '%s(%s)' % (rec.name, ', '.join(children_names))
class ComputeCascade(models.Model):
_name = 'test_new_api.cascade'
_description = 'Test New API Cascade'
@@ -22,6 +22,7 @@ access_test_new_api_compute_inverse,access_test_new_api_compute_inverse,model_te
access_test_new_api_compute_readonly,access_test_new_api_compute_readonly,model_test_new_api_compute_readonly,,1,1,1,1
access_test_new_api_multi_compute_inverse,access_test_new_api_multi_compute_inverse,model_test_new_api_multi_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_recursive_tree,access_test_new_api_recursive_tree,model_test_new_api_recursive_tree,,1,1,1,1
access_test_new_api_cascade,access_test_new_api_cascade,model_test_new_api_cascade,,1,1,1,1
access_test_new_api_compute_readwrite,access_test_new_api_compute_readwrite,model_test_new_api_compute_readwrite,,1,1,1,1
access_test_new_api_compute_onchange,access_test_new_api_compute_onchange,model_test_new_api_compute_onchange,,1,1,1,1
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
22 access_test_new_api_compute_readonly access_test_new_api_compute_readonly model_test_new_api_compute_readonly 1 1 1 1
23 access_test_new_api_multi_compute_inverse access_test_new_api_multi_compute_inverse model_test_new_api_multi_compute_inverse 1 1 1 1
24 access_test_new_api_recursive access_test_new_api_recursive model_test_new_api_recursive 1 1 1 1
25 access_test_new_api_recursive_tree access_test_new_api_recursive_tree model_test_new_api_recursive_tree 1 1 1 1
26 access_test_new_api_cascade access_test_new_api_cascade model_test_new_api_cascade 1 1 1 1
27 access_test_new_api_compute_readwrite access_test_new_api_compute_readwrite model_test_new_api_compute_readwrite 1 1 1 1
28 access_test_new_api_compute_onchange access_test_new_api_compute_onchange model_test_new_api_compute_onchange 1 1 1 1
@@ -495,6 +495,14 @@ class TestFields(TransactionCaseWithUserDemo):
b.unlink()
self.assertEqual((a + b + c + d).exists(), a)
def test_12_recursive_tree(self):
foo = self.env['test_new_api.recursive.tree'].create({'name': 'foo'})
self.assertEqual(foo.display_name, 'foo()')
bar = foo.create({'name': 'bar', 'parent_id': foo.id})
self.assertEqual(foo.display_name, 'foo(bar())')
baz = foo.create({'name': 'baz', 'parent_id': bar.id})
self.assertEqual(foo.display_name, 'foo(bar(baz()))')
def test_12_cascade(self):
""" test computed field depending on computed field """
message = self.env.ref('test_new_api.message_0_0')
+11 -12
View File
@@ -5782,36 +5782,35 @@ Fields:
tocompute = list(tocompute)
# process what to compute
for field, records in tocompute:
for field, records, create in tocompute:
records -= self.env.protected(field)
if not records:
continue
# Dont force the recomputation of compute fields which are
# not stored as this is not really necessary.
recursive = not create and field.recursive
if field.compute and field.store:
if recursive:
marked_records = self.env.not_to_compute(field, records)
if field.recursive:
recursively_marked = self.env.not_to_compute(field, records)
self.env.add_to_compute(field, records)
else:
if recursive:
marked_records = records & self.env.cache.get_records(records, field)
# Dont force the recomputation of compute fields which are
# not stored as this is not really necessary.
if field.recursive:
recursively_marked = records & self.env.cache.get_records(records, field)
self.env.cache.invalidate([(field, records._ids)])
# recursively trigger recomputation of field's dependents
if recursive:
marked_records.modified([field.name])
if field.recursive:
recursively_marked.modified([field.name], create)
def _modified_triggers(self, tree, create=False):
""" Return an iterator traversing a tree of field triggers on ``self``,
traversing backwards field dependencies along the way, and yielding
pairs ``(field, records)`` to recompute.
tuple ``(field, records, created)`` to recompute.
"""
if not self:
return
# first yield what to compute
for field in tree.get(None, ()):
yield field, self
yield field, self, create
# then traverse dependencies backwards, and proceed recursively
for key, val in tree.items():