[FIX] core: first onchange on new one2many line

Consider models `M` and `L`, with a one2many field `M.l_ids` with
inverse `L.m_id`.  Consider also a computed field `L.foo` that depends
on `L.m_id`.

The field `L.foo` is not computed correctly by `onchange` on a new line
in the form view of `M`: its value is forced to `False` because it has
no default, and is not recomputed by `onchange` since the field `L.m_id`
is never triggered an `onchange`.

Modify the method `onchange` to not force fields without a default to
`False`, and return all fields.
This commit is contained in:
Raphael Collet
2020-08-20 13:39:04 +00:00
parent 38b7462dcf
commit 3e91fa1dbf
4 changed files with 32 additions and 12 deletions
@@ -244,7 +244,7 @@ class MultiLine(models.Model):
multi = fields.Many2one('test_new_api.multi', ondelete='cascade')
name = fields.Char()
partner = fields.Many2one('res.partner')
partner = fields.Many2one(related='multi.partner', store=True)
tags = fields.Many2many('test_new_api.multi.tag')
@@ -378,6 +378,19 @@ class TestOnChange(SavepointCaseWithUserDemo):
result = Foo.onchange(values, 'value1', field_onchange)
self.assertEqual(result['value'], {})
def test_onchange_one2many_first(self):
partner = self.env['res.partner'].create({
'name': 'X',
'country_id': self.env.ref('base.be').id,
})
with common.Form(self.env['test_new_api.multi']) as form:
form.partner = partner
self.assertEqual(form.partner, partner)
self.assertEqual(form.name, partner.name)
with form.lines.new() as line:
# the first onchange() must have computed partner
self.assertEqual(line.partner, partner)
def test_onchange_one2many_value(self):
""" test the value of the one2many field inside the onchange """
discussion = self.env.ref('test_new_api.discussion_0')
+8 -1
View File
@@ -963,7 +963,14 @@ class Field(MetaField('DummyField', (object,), {})):
self.compute_value(recs)
except (AccessError, MissingError):
self.compute_value(record)
value = env.cache.get(record, self)
try:
value = env.cache.get(record, self)
except CacheMiss:
if self.readonly:
raise
# fallback to null value if compute gives nothing
value = self.convert_to_cache(False, record, validate=False)
env.cache.set(record, self, value)
elif self.type == 'many2one' and self.delegate and not record.id:
# parent record of a new record: new record, with the same
+10 -10
View File
@@ -5859,8 +5859,8 @@ Fields:
on_change attribute
When ``field_name`` is falsy, the method first adds default values
to ``values``, applies onchange methods to them, and return all the
fields in ``field_onchange``.
to ``values``, computes the remaining fields, applies onchange
methods to them, and return all the fields in ``field_onchange``.
"""
# this is for tests using `Form`
self.flush()
@@ -5902,11 +5902,12 @@ Fields:
""" A dict with the values of a record, following a prefix tree. """
__slots__ = ()
def __init__(self, record, tree):
def __init__(self, record, tree, fetch=True):
# put record in dict to include it when comparing snapshots
super(Snapshot, self).__init__({'<record>': record, '<tree>': tree})
for name in tree:
self.fetch(name)
if fetch:
for name in tree:
self.fetch(name)
def fetch(self, name):
""" Set the value of field ``name`` from the record's value. """
@@ -5920,6 +5921,8 @@ Fields:
def has_changed(self, name):
""" Return whether a field on record has changed. """
if name not in self:
return True
record = self['<record>']
subnames = self['<tree>'][name]
if record._fields[name].type not in ('one2many', 'many2many'):
@@ -5977,14 +5980,11 @@ Fields:
nametree = PrefixTree(self.browse(), field_onchange)
if first_call:
names = list(nametree)
values.update(self.default_get([
name
for name in names
for name in nametree
if name not in values
]))
for name in names:
values.setdefault(name, False)
# prefetch x2many lines without data (for the initial snapshot)
for name, subnames in nametree.items():
@@ -6032,7 +6032,7 @@ Fields:
record = self.new(initial_values, origin=self)
# make a snapshot based on the initial values of record
snapshot0 = Snapshot(record, nametree)
snapshot0 = Snapshot(record, nametree, fetch=(not first_call))
# store changed values in cache; also trigger recomputations based on
# subfields (e.g., line.a has been modified, line.b is computed stored