[FIX] core: first onchange should always compute fields
When a form view is opened, the first call to `onchange` should always compute fields, even if their dependencies have no default value. Make sure it is the case for main records, and for records inside one2many fields. The latter case was actually not working as expected. closes odoo/odoo#63646 X-original-commit: 45422d56bce413b8577f1784e10dd22ede93c751 Signed-off-by: Raphael Collet (rco) <rco@openerp.com>
This commit is contained in:
@@ -556,13 +556,13 @@ class ComputeOnchange(models.Model):
|
||||
@api.depends('foo')
|
||||
def _compute_bar(self):
|
||||
for record in self:
|
||||
record.bar = record.foo
|
||||
record.bar = (record.foo or "") + "r"
|
||||
|
||||
@api.depends('active', 'foo')
|
||||
def _compute_baz(self):
|
||||
for record in self:
|
||||
if record.active:
|
||||
record.baz = record.foo
|
||||
record.baz = (record.foo or "") + "z"
|
||||
|
||||
@api.depends('foo')
|
||||
def _compute_line_ids(self):
|
||||
@@ -590,9 +590,14 @@ class ComputeOnchangeLine(models.Model):
|
||||
_name = 'test_new_api.compute.onchange.line'
|
||||
_description = "Line-like model for test_new_api.compute.onchange"
|
||||
|
||||
record_id = fields.Many2one('test_new_api.compute.onchange', ondelete='cascade')
|
||||
foo = fields.Char()
|
||||
record_id = fields.Many2one('test_new_api.compute.onchange',
|
||||
required=True, ondelete='cascade')
|
||||
bar = fields.Char(compute='_compute_bar')
|
||||
|
||||
@api.depends('foo')
|
||||
def _compute_bar(self):
|
||||
for line in self:
|
||||
line.bar = (line.foo or "") + "r"
|
||||
|
||||
|
||||
class ComputeDynamicDepends(models.Model):
|
||||
|
||||
@@ -543,24 +543,29 @@ class TestComputeOnchange(common.TransactionCase):
|
||||
def test_create(self):
|
||||
model = self.env['test_new_api.compute.onchange']
|
||||
|
||||
# compute 'bar' (readonly) and 'baz' (editable)
|
||||
record = model.create({'active': True})
|
||||
self.assertEqual(record.bar, "r")
|
||||
self.assertEqual(record.baz, "z")
|
||||
|
||||
# compute 'bar' and 'baz'
|
||||
record = model.create({'active': True, 'foo': "foo"})
|
||||
self.assertEqual(record.bar, "foo")
|
||||
self.assertEqual(record.baz, "foo")
|
||||
self.assertEqual(record.bar, "foor")
|
||||
self.assertEqual(record.baz, "fooz")
|
||||
|
||||
# compute 'bar' but not 'baz'
|
||||
record = model.create({'active': True, 'foo': "foo", 'bar': "bar", 'baz': "baz"})
|
||||
self.assertEqual(record.bar, "foo")
|
||||
self.assertEqual(record.bar, "foor")
|
||||
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.bar, "foor")
|
||||
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.bar, "foor")
|
||||
self.assertEqual(record.baz, "baz")
|
||||
|
||||
def test_copy(self):
|
||||
@@ -572,10 +577,10 @@ class TestComputeOnchange(common.TransactionCase):
|
||||
{'name': 'bar1'},
|
||||
])
|
||||
|
||||
# compute 'bar', 'baz', 'line_ids' and 'tag_ids'
|
||||
# compute 'bar' (readonly), 'baz', 'line_ids' and 'tag_ids' (editable)
|
||||
record = Model.create({'active': True, 'foo': "foo1"})
|
||||
self.assertEqual(record.bar, "foo1")
|
||||
self.assertEqual(record.baz, "foo1")
|
||||
self.assertEqual(record.bar, "foo1r")
|
||||
self.assertEqual(record.baz, "foo1z")
|
||||
self.assertEqual(record.line_ids.mapped('foo'), ['foo1'])
|
||||
self.assertEqual(record.tag_ids, tag_foo)
|
||||
|
||||
@@ -585,7 +590,7 @@ class TestComputeOnchange(common.TransactionCase):
|
||||
'line_ids': [Command.create({'foo': 'bar'})],
|
||||
'tag_ids': [Command.link(tag_bar.id)],
|
||||
})
|
||||
self.assertEqual(record.bar, "foo1")
|
||||
self.assertEqual(record.bar, "foo1r")
|
||||
self.assertEqual(record.baz, "baz1")
|
||||
self.assertEqual(record.line_ids.mapped('foo'), ['foo1', 'bar'])
|
||||
self.assertEqual(record.tag_ids, tag_foo + tag_bar)
|
||||
@@ -593,7 +598,7 @@ class TestComputeOnchange(common.TransactionCase):
|
||||
# copy the record, and check results
|
||||
copied = record.copy()
|
||||
self.assertEqual(copied.foo, "foo1 (copy)") # copied and modified
|
||||
self.assertEqual(copied.bar, "foo1 (copy)") # computed
|
||||
self.assertEqual(copied.bar, "foo1 (copy)r") # computed
|
||||
self.assertEqual(copied.baz, "baz1") # copied
|
||||
self.assertEqual(record.line_ids.mapped('foo'), ['foo1', 'bar']) # copied
|
||||
self.assertEqual(record.tag_ids, tag_foo + tag_bar) # copied
|
||||
@@ -601,128 +606,141 @@ class TestComputeOnchange(common.TransactionCase):
|
||||
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")
|
||||
self.assertEqual(record.bar, "foor")
|
||||
self.assertEqual(record.baz, "fooz")
|
||||
|
||||
# recompute 'bar' and 'baz'
|
||||
# recompute 'bar' (readonly) and 'baz' (editable)
|
||||
record.write({'foo': "foo1"})
|
||||
self.assertEqual(record.bar, "foo1")
|
||||
self.assertEqual(record.baz, "foo1")
|
||||
self.assertEqual(record.bar, "foo1r")
|
||||
self.assertEqual(record.baz, "foo1z")
|
||||
|
||||
# recompute 'bar' but not 'baz'
|
||||
record.write({'foo': "foo2", 'bar': "bar2", 'baz': "baz2"})
|
||||
self.assertEqual(record.bar, "foo2")
|
||||
self.assertEqual(record.bar, "foo2r")
|
||||
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.bar, "foo3r")
|
||||
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.bar, "foo4r")
|
||||
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")
|
||||
self.assertEqual(record.bar, "foor")
|
||||
self.assertEqual(record.baz, "fooz")
|
||||
|
||||
# recompute 'bar' and 'baz'
|
||||
# recompute 'bar' (readonly) and 'baz' (editable)
|
||||
record.foo = "foo1"
|
||||
self.assertEqual(record.bar, "foo1")
|
||||
self.assertEqual(record.baz, "foo1")
|
||||
self.assertEqual(record.bar, "foo1r")
|
||||
self.assertEqual(record.baz, "foo1z")
|
||||
|
||||
# do not recompute 'baz'
|
||||
record.baz = "baz2"
|
||||
self.assertEqual(record.bar, "foo1")
|
||||
self.assertEqual(record.bar, "foo1r")
|
||||
self.assertEqual(record.baz, "baz2")
|
||||
|
||||
# recompute 'baz', but do not change its value
|
||||
record.active = False
|
||||
self.assertEqual(record.bar, "foo1")
|
||||
self.assertEqual(record.bar, "foo1r")
|
||||
self.assertEqual(record.baz, "baz2")
|
||||
|
||||
# recompute 'baz', but do not change its value
|
||||
record.foo = "foo3"
|
||||
self.assertEqual(record.bar, "foo3")
|
||||
self.assertEqual(record.bar, "foo3r")
|
||||
self.assertEqual(record.baz, "baz2")
|
||||
|
||||
# do not recompute 'baz'
|
||||
record.baz = "baz4"
|
||||
self.assertEqual(record.bar, "foo3")
|
||||
self.assertEqual(record.bar, "foo3r")
|
||||
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")
|
||||
record = model.new({'active': True})
|
||||
self.assertEqual(record.bar, "r")
|
||||
self.assertEqual(record.baz, "z")
|
||||
|
||||
# recompute 'bar' and 'baz'
|
||||
# recompute 'bar' (readonly) and 'baz' (editable)
|
||||
record.foo = "foo1"
|
||||
self.assertEqual(record.bar, "foo1")
|
||||
self.assertEqual(record.baz, "foo1")
|
||||
self.assertEqual(record.bar, "foo1r")
|
||||
self.assertEqual(record.baz, "foo1z")
|
||||
|
||||
# do not recompute 'baz'
|
||||
record.baz = "baz2"
|
||||
self.assertEqual(record.bar, "foo1")
|
||||
self.assertEqual(record.bar, "foo1r")
|
||||
self.assertEqual(record.baz, "baz2")
|
||||
|
||||
# recompute 'baz', but do not change its value
|
||||
record.active = False
|
||||
self.assertEqual(record.bar, "foo1")
|
||||
self.assertEqual(record.bar, "foo1r")
|
||||
self.assertEqual(record.baz, "baz2")
|
||||
|
||||
# recompute 'baz', but do not change its value
|
||||
record.foo = "foo3"
|
||||
self.assertEqual(record.bar, "foo3")
|
||||
self.assertEqual(record.bar, "foo3r")
|
||||
self.assertEqual(record.baz, "baz2")
|
||||
|
||||
# do not recompute 'baz'
|
||||
record.baz = "baz4"
|
||||
self.assertEqual(record.bar, "foo3")
|
||||
self.assertEqual(record.bar, "foo3r")
|
||||
self.assertEqual(record.baz, "baz4")
|
||||
|
||||
def test_onchange(self):
|
||||
# check computations of 'bar' (readonly) and 'baz' (editable)
|
||||
form = common.Form(self.env['test_new_api.compute.onchange'])
|
||||
self.assertEqual(form.bar, "r")
|
||||
self.assertEqual(form.baz, False)
|
||||
form.active = True
|
||||
self.assertEqual(form.bar, "r")
|
||||
self.assertEqual(form.baz, "z")
|
||||
form.foo = "foo1"
|
||||
self.assertEqual(form.bar, "foo1")
|
||||
self.assertEqual(form.baz, "foo1")
|
||||
self.assertEqual(form.bar, "foo1r")
|
||||
self.assertEqual(form.baz, "foo1z")
|
||||
form.baz = "baz2"
|
||||
self.assertEqual(form.bar, "foo1")
|
||||
self.assertEqual(form.bar, "foo1r")
|
||||
self.assertEqual(form.baz, "baz2")
|
||||
form.active = False
|
||||
self.assertEqual(form.bar, "foo1")
|
||||
self.assertEqual(form.bar, "foo1r")
|
||||
self.assertEqual(form.baz, "baz2")
|
||||
form.foo = "foo3"
|
||||
self.assertEqual(form.bar, "foo3")
|
||||
self.assertEqual(form.bar, "foo3r")
|
||||
self.assertEqual(form.baz, "baz2")
|
||||
form.active = True
|
||||
self.assertEqual(form.bar, "foo3")
|
||||
self.assertEqual(form.baz, "foo3")
|
||||
self.assertEqual(form.bar, "foo3r")
|
||||
self.assertEqual(form.baz, "foo3z")
|
||||
|
||||
with form.line_ids.new() as line:
|
||||
# check computation of 'bar' (readonly)
|
||||
self.assertEqual(line.foo, False)
|
||||
self.assertEqual(line.bar, "r")
|
||||
line.foo = "foo"
|
||||
self.assertEqual(line.foo, "foo")
|
||||
self.assertEqual(line.bar, "foor")
|
||||
|
||||
record = form.save()
|
||||
self.assertEqual(record.bar, "foo3")
|
||||
self.assertEqual(record.baz, "foo3")
|
||||
self.assertEqual(record.bar, "foo3r")
|
||||
self.assertEqual(record.baz, "foo3z")
|
||||
|
||||
form = common.Form(record)
|
||||
self.assertEqual(form.bar, "foo3")
|
||||
self.assertEqual(form.baz, "foo3")
|
||||
self.assertEqual(form.bar, "foo3r")
|
||||
self.assertEqual(form.baz, "foo3z")
|
||||
form.foo = "foo4"
|
||||
self.assertEqual(form.bar, "foo4")
|
||||
self.assertEqual(form.baz, "foo4")
|
||||
self.assertEqual(form.bar, "foo4r")
|
||||
self.assertEqual(form.baz, "foo4z")
|
||||
form.baz = "baz5"
|
||||
self.assertEqual(form.bar, "foo4")
|
||||
self.assertEqual(form.bar, "foo4r")
|
||||
self.assertEqual(form.baz, "baz5")
|
||||
form.active = False
|
||||
self.assertEqual(form.bar, "foo4")
|
||||
self.assertEqual(form.bar, "foo4r")
|
||||
self.assertEqual(form.baz, "baz5")
|
||||
form.foo = "foo6"
|
||||
self.assertEqual(form.bar, "foo6")
|
||||
self.assertEqual(form.bar, "foo6r")
|
||||
self.assertEqual(form.baz, "baz5")
|
||||
|
||||
def test_onchange_one2many(self):
|
||||
|
||||
+5
-2
@@ -6162,8 +6162,11 @@ Fields:
|
||||
for name in names:
|
||||
snapshot0.fetch(name)
|
||||
|
||||
# determine which field(s) should be triggered an onchange
|
||||
todo = list(names or nametree)
|
||||
# Determine which field(s) should be triggered an onchange. On the first
|
||||
# call, 'names' only contains fields with a default. If 'self' is a new
|
||||
# line in a one2many field, 'names' also contains the one2many's inverse
|
||||
# field, and that field may not be in nametree.
|
||||
todo = list(names) + list(nametree) if first_call else list(names)
|
||||
done = set()
|
||||
|
||||
# dummy assignment: trigger invalidations on the record
|
||||
|
||||
Reference in New Issue
Block a user