From 55bd019a6282886182de2f99d7be54da5d298b2b Mon Sep 17 00:00:00 2001 From: Daniel Duque Date: Wed, 28 Oct 2020 10:38:01 +0000 Subject: [PATCH] [FIX] core: missing default values with inherits Consider some model A that "_inherits" from model B, which itself "_inherits" from model C. Also consider a field F on model C, which is inherited by both models B and A. Now create a record from model A, with a given record for model C, no record for model B, and a default value for field F. This should create a record in A, connected to a new record in B, connected to the given record in C, and the default value should be ignored, as a record from C is given. Before this commit, the given record in C is modified with the default value for F. The default value is considered because no record is given for B, and that value is used to modify the given record in C. The fix consists in discarding default values by considering potential ancestor records when parent records are not given. closes odoo/odoo#61165 X-original-commit: b83dfaa2c9bcc6bc7f8df19ed0e442554816709b Signed-off-by: Raphael Collet (rco) Co-authored-by: Raphael Collet --- odoo/addons/test_inherits/models.py | 2 ++ .../test_inherits/tests/test_inherits.py | 18 +++++++++++++ odoo/models.py | 27 ++++++++++++++----- 3 files changed, 41 insertions(+), 6 deletions(-) diff --git a/odoo/addons/test_inherits/models.py b/odoo/addons/test_inherits/models.py index c9eeff60904..b503e8c173b 100644 --- a/odoo/addons/test_inherits/models.py +++ b/odoo/addons/test_inherits/models.py @@ -13,6 +13,7 @@ class Unit(models.Model): surname = fields.Char(compute='_compute_surname') line_ids = fields.One2many('test.unit.line', 'unit_id') readonly_name = fields.Char('Readonly Name', readonly=True) + size = fields.Integer() @api.depends('name') def _compute_surname(self): @@ -38,6 +39,7 @@ class Box(models.Model): unit_id = fields.Many2one('test.unit', 'Unit', required=True, ondelete='cascade') field_in_box = fields.Char('Field1') + size = fields.Integer() # We add a third level of _inherits diff --git a/odoo/addons/test_inherits/tests/test_inherits.py b/odoo/addons/test_inherits/tests/test_inherits.py index 4c32db71864..9822ab19020 100644 --- a/odoo/addons/test_inherits/tests/test_inherits.py +++ b/odoo/addons/test_inherits/tests/test_inherits.py @@ -17,6 +17,24 @@ class test_inherits(common.TransactionCase): self.assertEqual(pallet.field_in_box, 'box') self.assertEqual(pallet.field_in_pallet, 'pallet') + def test_create_3_levels_inherits_with_defaults(self): + unit = self.env['test.unit'].create({ + 'name': 'U', + 'state': 'a', + 'size': 1, + }) + ctx = { + 'default_state': 'b', # 'state' is inherited from 'test.unit' + 'default_size': 2, # 'size' is inherited from 'test.box' + } + pallet = self.env['test.pallet'].with_context(ctx).create({ + 'name': 'P', + 'unit_id': unit.id, # grand-parent field is set + }) + # default 'state' should be ignored, but default 'size' should not + self.assertEqual(pallet.state, 'a') + self.assertEqual(pallet.size, 2) + def test_read_3_levels_inherits(self): """ Check that we can read an inherited field on 3 levels """ pallet = self.env.ref('test_inherits.pallet_a') diff --git a/odoo/models.py b/odoo/models.py index 0a3a08109a2..6489e13e65d 100644 --- a/odoo/models.py +++ b/odoo/models.py @@ -1807,18 +1807,33 @@ class BaseModel(MetaModel('DummyModel', (object,), {'_register': False})): @api.model def _add_missing_default_values(self, values): # avoid overriding inherited values when parent is set - avoid_models = { - parent_model - for parent_model, parent_field in self._inherits.items() - if parent_field in values - } + avoid_models = set() + + def collect_models_to_avoid(model): + for parent_mname, parent_fname in model._inherits.items(): + if parent_fname in values: + avoid_models.add(parent_mname) + else: + # manage the case where an ancestor parent field is set + collect_models_to_avoid(self.env[parent_mname]) + + collect_models_to_avoid(self) + + def avoid(field): + # check whether the field is inherited from one of avoid_models + if avoid_models: + while field.inherited: + field = field.related_field + if field.model_name in avoid_models: + return True + return False # compute missing fields missing_defaults = { name for name, field in self._fields.items() if name not in values - if not (field.inherited and field.related_field.model_name in avoid_models) + if not avoid(field) } if not missing_defaults: