[FIX] ir_model: cascade delete custom o2m and related fields
Before this revision: * Install a module, for this example we'll use `project` * Create a o2m field or a related field whose inverse is a field from the `project` module. * Uninstall the `project` module. Result -> BOOM! The registry can't be reloaded because it can't find the inverse field of our custom field, ayayay!! After this revision, this shouldn't break the registry anymore because we create two m2o fields which will trigger cascade deletes for both o2m and related fields upon the deletion of the inverse field, these fields are both computed stored fields and they're not shown to the end user, this is all done "under the rug". Task-ID: 58808
This commit is contained in:
@@ -308,6 +308,8 @@ class IrModelFields(models.Model):
|
||||
relation = fields.Char(string='Object Relation',
|
||||
help="For relationship fields, the technical name of the target model")
|
||||
relation_field = fields.Char(help="For one2many fields, the field on the target model that implement the opposite many2one relationship")
|
||||
relation_field_id = fields.Many2one('ir.model.fields', compute='_compute_relation_field_id',
|
||||
store=True, ondelete='cascade', string='Relation field')
|
||||
model_id = fields.Many2one('ir.model', string='Model', required=True, index=True, ondelete='cascade',
|
||||
help="The model this field belongs to")
|
||||
field_description = fields.Char(string='Field Label', default='', required=True, translate=True)
|
||||
@@ -319,6 +321,8 @@ class IrModelFields(models.Model):
|
||||
"For example: [('blue','Blue'),('yellow','Yellow')]")
|
||||
copy = fields.Boolean(string='Copied', help="Whether the value is copied when duplicating a record.")
|
||||
related = fields.Char(string='Related Field', help="The corresponding related field, if any. This must be a dot-separated list of field names.")
|
||||
related_field_id = fields.Many2one('ir.model.fields', compute='_compute_related_field_id',
|
||||
store=True, string="Related field", ondelete='cascade')
|
||||
required = fields.Boolean()
|
||||
readonly = fields.Boolean()
|
||||
index = fields.Boolean(string='Indexed')
|
||||
@@ -346,6 +350,19 @@ class IrModelFields(models.Model):
|
||||
" name, partner_id.name")
|
||||
store = fields.Boolean(string='Stored', default=True, help="Whether the value is stored in the database.")
|
||||
|
||||
@api.depends('relation', 'relation_field')
|
||||
def _compute_relation_field_id(self):
|
||||
for rec in self:
|
||||
if rec.state == 'manual' and rec.relation_field:
|
||||
rec.relation_field_id = self._get(rec.relation, rec.relation_field)
|
||||
|
||||
@api.depends('related')
|
||||
def _compute_related_field_id(self):
|
||||
for rec in self:
|
||||
if rec.state == 'manual' and rec.related:
|
||||
field = rec._related_field()
|
||||
rec.related_field_id = self._get(field.model_name, field.name)
|
||||
|
||||
@api.depends()
|
||||
def _in_modules(self):
|
||||
installed_modules = self.env['ir.module.module'].search([('state', '=', 'installed')])
|
||||
|
||||
@@ -230,6 +230,7 @@ class TestActionBindings(common.TransactionCase):
|
||||
|
||||
class TestCustomFields(common.TransactionCase):
|
||||
MODEL = 'res.partner'
|
||||
COMODEL = 'res.users'
|
||||
|
||||
def setUp(self):
|
||||
# check that the registry is properly reset
|
||||
@@ -273,6 +274,48 @@ class TestCustomFields(common.TransactionCase):
|
||||
with self.assertRaises(ValidationError):
|
||||
self.create_field('foo')
|
||||
|
||||
def test_create_custom_o2m(self):
|
||||
""" try creating a custom o2m and then deleting its m2o inverse """
|
||||
model = self.env['ir.model'].search([('model', '=', self.MODEL)])
|
||||
comodel = self.env['ir.model'].search([('model', '=', self.COMODEL)])
|
||||
|
||||
m2o_field = self.env['ir.model.fields'].create({
|
||||
'model_id': comodel.id,
|
||||
'name': 'x_my_m2o',
|
||||
'field_description': 'my_m2o',
|
||||
'ttype': 'many2one',
|
||||
'relation': self.MODEL,
|
||||
})
|
||||
|
||||
o2m_field = self.env['ir.model.fields'].create({
|
||||
'model_id': model.id,
|
||||
'name': 'x_my_o2m',
|
||||
'field_description': 'my_o2m',
|
||||
'ttype': 'one2many',
|
||||
'relation': self.COMODEL,
|
||||
'relation_field': m2o_field.name,
|
||||
})
|
||||
|
||||
m2o_field.unlink()
|
||||
self.assertFalse(o2m_field.exists())
|
||||
|
||||
def test_create_custom_related(self):
|
||||
""" try creating a custom related then deleting its inverse """
|
||||
comodel = self.env['ir.model'].search([('model', '=', self.COMODEL)])
|
||||
|
||||
field = self.create_field('x_my_char')
|
||||
|
||||
related_field = self.env['ir.model.fields'].create({
|
||||
'model_id': comodel.id,
|
||||
'name': 'x_oh_boy',
|
||||
'field_description': 'x_oh_boy',
|
||||
'ttype': 'char',
|
||||
'related': 'partner_id.x_my_char',
|
||||
})
|
||||
|
||||
field.unlink()
|
||||
self.assertFalse(related_field.exists())
|
||||
|
||||
def test_rename_custom(self):
|
||||
""" custom field names must be start with 'x_' """
|
||||
field = self.create_field('x_foo')
|
||||
|
||||
Reference in New Issue
Block a user