[FIX] fields: default to ondelete='cascade' for required m2o fields on transient

TransientModel (wizards) can be annoying because if they have required
many2one fields, those fields will default to `ondelete='restrict'`,
preventing user to delete the comodel records without deleting the
transient model records first.

To improve such case, we default to `ondelete='cascade'` for required
many2one field on a TransientModel (unless specified otherwise)

closes odoo/odoo#36738

Signed-off-by: Raphael Collet (rco) <rco@openerp.com>
This commit is contained in:
Xavier ALT
2019-09-20 12:23:09 +00:00
committed by Raphael Collet
parent 1a3dcfeae6
commit c5fa4771fc
3 changed files with 38 additions and 6 deletions
+8
View File
@@ -594,6 +594,14 @@ class RequiredM2O(models.Model):
bar = fields.Many2one('res.country', required=True)
class RequiredM2OTransient(models.TransientModel):
_name = 'test_new_api.req_m2o_transient'
_description = 'Transient Model with Required Many2one'
foo = fields.Many2one('res.currency', required=True, ondelete='restrict')
bar = fields.Many2one('res.country', required=True)
class Attachment(models.Model):
_name = 'test_new_api.attachment'
_description = 'Attachment'
@@ -1929,3 +1929,25 @@ class TestRequiredMany2one(common.TransactionCase):
with self.assertRaises(ValueError):
field._setup_regular_base(Model)
class TestRequiredMany2oneTransient(common.TransactionCase):
def test_explicit_ondelete(self):
field = self.env['test_new_api.req_m2o_transient']._fields['foo']
self.assertEqual(field.ondelete, 'restrict')
def test_implicit_ondelete(self):
field = self.env['test_new_api.req_m2o_transient']._fields['bar']
self.assertEqual(field.ondelete, 'cascade')
def test_explicit_set_null(self):
Model = self.env['test_new_api.req_m2o_transient']
field = Model._fields['foo']
# invalidate registry to redo the setup afterwards
self.registry.registry_invalidated = True
self.patch(field, 'ondelete', 'set null')
with self.assertRaises(ValueError):
field._setup_regular_base(Model)
+8 -6
View File
@@ -2306,7 +2306,14 @@ class Many2one(_Relational):
# 3) The ondelete attribute is explicitly defined as 'set null' for a required m2o,
# this is considered a programming error.
if not self.ondelete:
self.ondelete = 'restrict' if self.required else 'set null'
comodel = model.env[self.comodel_name]
if model.is_transient() and not comodel.is_transient():
# Many2one relations from TransientModel Model are annoying because
# they can block deletion due to foreign keys. So unless stated
# otherwise, we default them to ondelete='cascade'.
self.ondelete = 'cascade' if self.required else 'set null'
else:
self.ondelete = 'restrict' if self.required else 'set null'
if self.ondelete == 'set null' and self.required:
raise ValueError(
"The m2o field %s of model %s is required but declares its ondelete policy "
@@ -2318,11 +2325,6 @@ class Many2one(_Relational):
comodel = model.env[self.comodel_name]
if not model.is_transient() and comodel.is_transient():
raise ValueError('Many2one %s from Model to TransientModel is forbidden' % self)
if model.is_transient() and not comodel.is_transient():
# Many2one relations from TransientModel Model are annoying because
# they can block deletion due to foreign keys. So unless stated
# otherwise, we default them to ondelete='cascade'.
self.ondelete = self.ondelete or 'cascade'
return super(Many2one, self).update_db(model, columns)
def update_db_column(self, model, column):