diff --git a/odoo/addons/base/models/ir_model.py b/odoo/addons/base/models/ir_model.py index 96187c76a32..0176b2a8ed2 100644 --- a/odoo/addons/base/models/ir_model.py +++ b/odoo/addons/base/models/ir_model.py @@ -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')]) diff --git a/odoo/addons/base/tests/test_ir_actions.py b/odoo/addons/base/tests/test_ir_actions.py index 355ddaa143d..75982358d6c 100644 --- a/odoo/addons/base/tests/test_ir_actions.py +++ b/odoo/addons/base/tests/test_ir_actions.py @@ -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')