diff --git a/odoo/addons/test_new_api/models/test_new_api.py b/odoo/addons/test_new_api/models/test_new_api.py index 602173c4145..99f0fd65b68 100644 --- a/odoo/addons/test_new_api/models/test_new_api.py +++ b/odoo/addons/test_new_api/models/test_new_api.py @@ -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') diff --git a/odoo/addons/test_new_api/tests/test_onchange.py b/odoo/addons/test_new_api/tests/test_onchange.py index 5bffeae5f86..38c7b8be531 100644 --- a/odoo/addons/test_new_api/tests/test_onchange.py +++ b/odoo/addons/test_new_api/tests/test_onchange.py @@ -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') diff --git a/odoo/fields.py b/odoo/fields.py index 46d65d4e6f4..2838bfbef56 100644 --- a/odoo/fields.py +++ b/odoo/fields.py @@ -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 diff --git a/odoo/models.py b/odoo/models.py index f3a2b4d7530..991573a4155 100644 --- a/odoo/models.py +++ b/odoo/models.py @@ -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, '': 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[''] subnames = self[''][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