[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) <rco@openerp.com>
Co-authored-by: Raphael Collet <rco@odoo.com>
This commit is contained in:
Daniel Duque
2020-11-02 13:53:36 +00:00
committed by Raphael Collet
co-authored by Raphael Collet
parent 51ed48b1b6
commit 55bd019a62
3 changed files with 41 additions and 6 deletions
+2
View File
@@ -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
@@ -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')
+21 -6
View File
@@ -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: