[FIX] models: compute-onchange

This commit is contained in:
Raphael Collet
2019-09-03 12:02:45 +00:00
parent 0518b23127
commit cb2117a487
4 changed files with 182 additions and 11 deletions
@@ -23,6 +23,7 @@ access_test_new_api_compute_protected,access_test_new_api_compute_protected,mode
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_cascade,access_test_new_api_cascade,model_test_new_api_cascade,,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
access_test_new_api_binary_svg,access_test_new_api_binary_svg,model_test_new_api_binary_svg,,1,1,1,1
access_test_new_api_monetary_base,access_test_new_api_monetary_base,model_test_new_api_monetary_base,,1,1,1,1
access_test_new_api_monetary_related,access_test_new_api_monetary_related,model_test_new_api_monetary_related,,1,1,1,1
1 id name model_id:id group_id:id perm_read perm_write perm_create perm_unlink
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_cascade access_test_new_api_cascade model_test_new_api_cascade 1 1 1 1
26 access_test_new_api_compute_onchange access_test_new_api_compute_onchange model_test_new_api_compute_onchange 1 1 1 1
27 access_test_new_api_binary_svg access_test_new_api_binary_svg model_test_new_api_binary_svg 1 1 1 1
28 access_test_new_api_monetary_base access_test_new_api_monetary_base model_test_new_api_monetary_base 1 1 1 1
29 access_test_new_api_monetary_related access_test_new_api_monetary_related model_test_new_api_monetary_related 1 1 1 1
+23 -2
View File
@@ -373,8 +373,8 @@ class Related(models.Model):
related_related_name = fields.Char(related='related_name', string='A related on a related on Name', readonly=False)
message = fields.Many2one('test_new_api.message')
message_name = fields.Text(related="message.body", related_sudo=False, string='Message Body', readonly=False)
message_currency = fields.Many2one(related="message.author", string='Message Author', readonly=False)
message_name = fields.Text(related="message.body", related_sudo=False, string='Message Body')
message_currency = fields.Many2one(related="message.author", string='Message Author')
class ComputeProtected(models.Model):
_name = 'test_new_api.compute.protected'
@@ -495,6 +495,27 @@ class ComputeCascade(models.Model):
record.baz = "<%s>" % (record.bar or "")
class ComputeOnchange(models.Model):
_name = 'test_new_api.compute.onchange'
_description = "Compute method as an onchange"
active = fields.Boolean()
foo = fields.Char()
bar = fields.Char(compute='_compute_bar', store=True)
baz = fields.Char(compute='_compute_baz', store=True, readonly=False)
@api.depends('foo')
def _compute_bar(self):
for record in self:
record.bar = record.foo
@api.depends('active', 'foo')
def _compute_baz(self):
for record in self:
if record.active:
record.baz = record.foo
class ModelImage(models.Model):
_name = 'test_new_api.model_image'
_description = 'Test Image field'
@@ -522,3 +522,156 @@ class TestOnChange(common.TransactionCase):
self.Message.onchange(values, 'discussion', field_onchange)
self.assertFalse(called[0], "discussion.messages has been read")
class TestComputeOnchange(common.TransactionCase):
def test_create(self):
model = self.env['test_new_api.compute.onchange']
# compute 'bar' and 'baz'
record = model.create({'active': True, 'foo': "foo"})
self.assertEqual(record.bar, "foo")
self.assertEqual(record.baz, "foo")
# compute 'bar' but not 'baz'
record = model.create({'active': True, 'foo': "foo", 'bar': "bar", 'baz': "baz"})
self.assertEqual(record.bar, "foo")
self.assertEqual(record.baz, "baz")
# compute 'bar' and 'baz', but do not change its value
record = model.create({'active': False, 'foo': "foo"})
self.assertEqual(record.bar, "foo")
self.assertEqual(record.baz, False)
# compute 'bar' but not 'baz'
record = model.create({'active': False, 'foo': "foo", 'bar': "bar", 'baz': "baz"})
self.assertEqual(record.bar, "foo")
self.assertEqual(record.baz, "baz")
def test_write(self):
model = self.env['test_new_api.compute.onchange']
record = model.create({'active': True, 'foo': "foo"})
self.assertEqual(record.bar, "foo")
self.assertEqual(record.baz, "foo")
# recompute 'bar' and 'baz'
record.write({'foo': "foo1"})
self.assertEqual(record.bar, "foo1")
self.assertEqual(record.baz, "foo1")
# recompute 'bar' but not 'baz'
record.write({'foo': "foo2", 'bar': "bar2", 'baz': "baz2"})
self.assertEqual(record.bar, "foo2")
self.assertEqual(record.baz, "baz2")
# recompute 'bar' and 'baz', but do not change its value
record.write({'active': False, 'foo': "foo3"})
self.assertEqual(record.bar, "foo3")
self.assertEqual(record.baz, "baz2")
# recompute 'bar' but not 'baz'
record.write({'active': False, 'foo': "foo4", 'bar': "bar4", 'baz': "baz4"})
self.assertEqual(record.bar, "foo4")
self.assertEqual(record.baz, "baz4")
def test_set(self):
model = self.env['test_new_api.compute.onchange']
record = model.create({'active': True, 'foo': "foo"})
self.assertEqual(record.bar, "foo")
self.assertEqual(record.baz, "foo")
# recompute 'bar' and 'baz'
record.foo = "foo1"
self.assertEqual(record.bar, "foo1")
self.assertEqual(record.baz, "foo1")
# do not recompute 'baz'
record.baz = "baz2"
self.assertEqual(record.bar, "foo1")
self.assertEqual(record.baz, "baz2")
# recompute 'baz', but do not change its value
record.active = False
self.assertEqual(record.bar, "foo1")
self.assertEqual(record.baz, "baz2")
# recompute 'baz', but do not change its value
record.foo = "foo3"
self.assertEqual(record.bar, "foo3")
self.assertEqual(record.baz, "baz2")
# do not recompute 'baz'
record.baz = "baz4"
self.assertEqual(record.bar, "foo3")
self.assertEqual(record.baz, "baz4")
def test_set_new(self):
model = self.env['test_new_api.compute.onchange']
record = model.new({'active': True, 'foo': "foo"})
self.assertEqual(record.bar, "foo")
self.assertEqual(record.baz, "foo")
# recompute 'bar' and 'baz'
record.foo = "foo1"
self.assertEqual(record.bar, "foo1")
self.assertEqual(record.baz, "foo1")
# do not recompute 'baz'
record.baz = "baz2"
self.assertEqual(record.bar, "foo1")
self.assertEqual(record.baz, "baz2")
# recompute 'baz', but do not change its value
record.active = False
self.assertEqual(record.bar, "foo1")
self.assertEqual(record.baz, "baz2")
# recompute 'baz', but do not change its value
record.foo = "foo3"
self.assertEqual(record.bar, "foo3")
self.assertEqual(record.baz, "baz2")
# do not recompute 'baz'
record.baz = "baz4"
self.assertEqual(record.bar, "foo3")
self.assertEqual(record.baz, "baz4")
def test_onchange(self):
form = common.Form(self.env['test_new_api.compute.onchange'])
form.active = True
form.foo = "foo1"
self.assertEqual(form.bar, "foo1")
self.assertEqual(form.baz, "foo1")
form.baz = "baz2"
self.assertEqual(form.bar, "foo1")
self.assertEqual(form.baz, "baz2")
form.active = False
self.assertEqual(form.bar, "foo1")
self.assertEqual(form.baz, "baz2")
form.foo = "foo3"
self.assertEqual(form.bar, "foo3")
self.assertEqual(form.baz, "baz2")
form.active = True
self.assertEqual(form.bar, "foo3")
self.assertEqual(form.baz, "foo3")
record = form.save()
self.assertEqual(record.bar, "foo3")
self.assertEqual(record.baz, "foo3")
form = common.Form(record)
self.assertEqual(form.bar, "foo3")
self.assertEqual(form.baz, "foo3")
form.foo = "foo4"
self.assertEqual(form.bar, "foo4")
self.assertEqual(form.baz, "foo4")
form.baz = "baz5"
self.assertEqual(form.bar, "foo4")
self.assertEqual(form.baz, "baz5")
form.active = False
self.assertEqual(form.bar, "foo4")
self.assertEqual(form.baz, "baz5")
form.foo = "foo6"
self.assertEqual(form.bar, "foo6")
self.assertEqual(form.baz, "baz5")
+5 -9
View File
@@ -3315,7 +3315,8 @@ Fields:
records_to_inverse[field] = self.filtered('id')
if field.relational or self._field_inverses[field]:
relational_names.append(fname)
protected.update(self._field_computed.get(field, [field]))
if field.compute and not field.readonly:
protected.update(self._field_computed.get(field, [field]))
# protect fields being written against recomputation
with env.protecting(protected, self):
@@ -3516,11 +3517,8 @@ Fields:
elif field.inverse:
inversed[key] = val
inversed_fields.add(field)
# ignore the protection of compute fields which do not have an
# inverse, otherwise their computation are not correctly
# performed, neither the fields which are computed in the same
# compute method (`test_validation_error`)
if not field.compute or field.inverse:
# protect non-readonly computed fields against (re)computation
if field.compute and not field.readonly:
protected.update(self._field_computed.get(field, [field]))
data_list.append(data)
@@ -5452,9 +5450,7 @@ Fields:
# Dont force the recomputation of compute fields which are
# not stored as this is not really necessary.
if field.compute and field.store:
records_to_invalidate = records.filtered(lambda r: not r.id)
self.env.add_to_compute(field, records - records_to_invalidate)
self.env.cache.invalidate([(field, records_to_invalidate._ids)])
self.env.add_to_compute(field, records)
else:
self.env.cache.invalidate([(field, records._ids)])
# recursively trigger recomputation of field's dependents