diff --git a/odoo/addons/base/models/ir_model.py b/odoo/addons/base/models/ir_model.py index 53ae02f6d5d..cd726c77f29 100644 --- a/odoo/addons/base/models/ir_model.py +++ b/odoo/addons/base/models/ir_model.py @@ -893,7 +893,7 @@ class IrModelFields(models.Model): 'index': bool(field.index), 'store': bool(field.store), 'copied': bool(field.copy), - 'on_delete': getattr(field, 'ondelete', None), + 'on_delete': field.ondelete if field.type == 'many2one' else None, 'related': ".".join(field.related) if field.related else None, 'readonly': bool(field.readonly), 'required': bool(field.required), @@ -1238,16 +1238,7 @@ class IrModelSelection(models.Model): 'Please modify them through Python code, ' 'preferably through a custom addon!')) - for selection in self: - if selection.field_id.store and \ - not self.env[selection.field_id.model]._abstract: - # replace the value by NULL in the field's corresponding column - query = 'UPDATE "{table}" SET "{field}"=NULL WHERE "{field}"=%s'.format( - table=self.env[selection.field_id.model]._table, - field=selection.field_id.name, - ) - self.env.cr.execute(query, [selection.value]) - + self._process_ondelete() result = super().unlink() # Reload registry for normal unlink only. For module uninstall, the @@ -1259,6 +1250,40 @@ class IrModelSelection(models.Model): return result + def _process_ondelete(self): + """ Process the 'ondelete' of the given selection values. """ + for selection in self: + Model = self.env[selection.field_id.model] + field = Model._fields[selection.field_id.name] + if not field.store or Model._abstract: + continue + + ondelete = (field.ondelete or {}).get(selection.value) or 'set null' + if callable(ondelete): + ondelete(selection._get_records()) + elif ondelete == 'set null': + selection._get_records().write({field.name: False}) + elif ondelete == 'set default': + value = field.convert_to_write(field.default(Model), Model) + selection._get_records().write({field.name: value}) + elif ondelete == 'cascade': + selection._get_records().unlink() + else: + # this shouldn't happen... simply a sanity check + raise ValueError(_( + "The ondelete policy %r is not valid for field %r" + ) % (ondelete, selection)) + + def _get_records(self): + """ Return the records having 'self' as a value. """ + self.ensure_one() + Model = self.env[self.field_id.model] + query = 'SELECT id FROM "{table}" WHERE "{field}"=%s'.format( + table=Model._table, field=self.field_id.name, + ) + self.env.cr.execute(query, [self.value]) + return Model.browse(r[0] for r in self.env.cr.fetchall()) + class IrModelConstraint(models.Model): """ 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 92887d66d68..5a192d755a5 100644 --- a/odoo/addons/test_new_api/models/test_new_api.py +++ b/odoo/addons/test_new_api/models/test_new_api.py @@ -894,3 +894,100 @@ class StateMixin(models.AbstractModel): ('confirmed', 'Confirmed'), ('done', 'Done'), ]) + + +class SelectionBase(models.Model): + _name = 'test_new_api.model_selection_base' + _description = "Model with a base selection field" + + my_selection = fields.Selection([ + ('foo', "Foo"), + ('bar', "Bar"), + ]) + + +class SelectionBaseNullExplicit(models.Model): + _inherit = 'test_new_api.model_selection_base' + _description = "Model with a selection field extension with ondelete null" + + my_selection = fields.Selection(selection_add=[ + ('quux', "Quux"), + ], ondelete={'quux': 'set null'}) + + +class SelectionBaseNullImplicit(models.Model): + _inherit = 'test_new_api.model_selection_base' + _description = "Model with a selection field extension without ondelete" + + my_selection = fields.Selection(selection_add=[ + ('ham', "Ham"), + ]) + + +class SelectionRequired(models.Model): + _name = 'test_new_api.model_selection_required' + _description = "Model with a required selection field" + + active = fields.Boolean(default=True) + my_selection = fields.Selection([ + ('foo', "Foo"), + ('bar', "Bar"), + ], required=True, default='foo') + + +class SelectionRequiredDefault(models.Model): + _inherit = 'test_new_api.model_selection_required' + _description = "Model with a selection field extension with ondelete default" + + my_selection = fields.Selection(selection_add=[ + ('baz', "Baz"), + ], ondelete={'baz': 'set default'}) + + +class SelectionRequiredCascade(models.Model): + _inherit = 'test_new_api.model_selection_required' + _description = "Model with a selection field extension with ondelete cascade" + + my_selection = fields.Selection(selection_add=[ + ('eggs', "Eggs"), + ], ondelete={'eggs': 'cascade'}) + + +class SelectionRequiredLiteral(models.Model): + _inherit = 'test_new_api.model_selection_required' + _description = "Model with a selection field extension with ondelete set