[FIX] core: cache consistency of one2many field with computed inverse

Consider a one2many field with a corresponding many2one field that is
computed.  After modifying the many2one field's dependencies, the cached
value of the one2many field is inconsistent until the many2one field has
been recomputed.  Therefore, one has to force the computation of the
many2one field before accessing the cached value of the one2many field.

closes odoo/odoo#59034

X-original-commit: b9201ebdd65eaabab9257cf97c58410681783fa6
Signed-off-by: Xavier Dollé (xdo) <xdo@odoo.com>
Signed-off-by: Raphael Collet (rco) <rco@openerp.com>
This commit is contained in:
Xavier-Do
2020-10-02 17:20:10 +00:00
committed by Raphael Collet
parent 0c7d16963b
commit 71549030c7
4 changed files with 39 additions and 0 deletions
@@ -1127,3 +1127,23 @@ class ShareCacheComputeLine(models.Model):
parent_id = fields.Many2one('test_new_api.model_shared_cache_compute_parent')
amount = fields.Integer()
user_id = fields.Many2one('res.users', default= lambda self: self.env.user) # Note: There is an ir.rule about this.
class ComputeContainer(models.Model):
_name = _description = 'test_new_api.compute.container'
name = fields.Char()
member_ids = fields.One2many('test_new_api.compute.member', 'container_id')
class ComputeMember(models.Model):
_name = _description = 'test_new_api.compute.member'
name = fields.Char()
container_id = fields.Many2one('test_new_api.compute.container', compute='_compute_container', store=True)
@api.depends('name')
def _compute_container(self):
container = self.env['test_new_api.compute.container']
for member in self:
member.container_id = container.search([('name', '=', member.name)], limit=1)
@@ -64,3 +64,5 @@ access_test_new_api_move,access_test_new_api_move,model_test_new_api_move,,1,1,1
access_test_new_api_move_line,access_test_new_api_move_line,model_test_new_api_move_line,,1,1,1,1
access_test_new_api_model_shared_cache_compute_parent,access_test_new_api.model_shared_cache_compute_parent,model_test_new_api_model_shared_cache_compute_parent,,1,1,1,1
access_test_new_api_model_shared_cache_compute_line,access_test_new_api.model_shared_cache_compute_line,model_test_new_api_model_shared_cache_compute_line,,1,1,1,1
access_test_new_api_compute_container,access_test_new_api_compute_container,model_test_new_api_compute_container,,1,1,1,1
access_test_new_api_compute_member,access_test_new_api_compute_member,model_test_new_api_compute_member,,1,1,1,1
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
64 access_test_new_api_move_line access_test_new_api_move_line model_test_new_api_move_line 1 1 1 1
65 access_test_new_api_model_shared_cache_compute_parent access_test_new_api.model_shared_cache_compute_parent model_test_new_api_model_shared_cache_compute_parent 1 1 1 1
66 access_test_new_api_model_shared_cache_compute_line access_test_new_api.model_shared_cache_compute_line model_test_new_api_model_shared_cache_compute_line 1 1 1 1
67 access_test_new_api_compute_container access_test_new_api_compute_container model_test_new_api_compute_container 1 1 1 1
68 access_test_new_api_compute_member access_test_new_api_compute_member model_test_new_api_compute_member 1 1 1 1
@@ -247,3 +247,11 @@ class One2manyCase(TransactionCase):
# delete parent, and check that recomputation ends
parent.unlink()
parent.flush()
def test_compute_stored_many2one_one2many(self):
container = self.env['test_new_api.compute.container'].create({'name': 'Foo'})
self.assertFalse(container.member_ids)
member = self.env['test_new_api.compute.member'].create({'name': 'Foo'})
# at this point, member.container_id must be computed for member to
# appear in container.member_ids
self.assertEqual(container.member_ids, member)
+9
View File
@@ -3055,6 +3055,15 @@ class One2many(_RelationalMulti):
domain = domain + [(inverse_field.model_field, '=', records._name)]
return domain
def __get__(self, records, owner):
if records is not None and self.inverse_name is not None:
# force the computation of the inverse field to ensure that the
# cache value of self is consistent
inverse_field = records.pool[self.comodel_name]._fields[self.inverse_name]
if inverse_field.compute:
records.env[self.comodel_name].recompute([self.inverse_name])
return super().__get__(records, owner)
def read(self, records):
# retrieve the lines in the comodel
context = {'active_test': False}