[FIX] ir_model: prevent renaming/removing fields that are used in views

This commit is contained in:
Raphael Collet
2017-04-06 15:06:06 +02:00
parent 5a1672fab8
commit 6621ade382
3 changed files with 59 additions and 2 deletions
+1 -1
View File
@@ -5015,7 +5015,7 @@ msgstr ""
#. module: base
#: code:addons/base/ir/ir_model.py:549
#, python-format
msgid "Cannot rename field to %s, because that field already exists!"
msgid "Cannot rename/delete fields that are still present in views:"
msgstr ""
#. module: base
+16
View File
@@ -9,6 +9,7 @@ from collections import defaultdict
from odoo import api, fields, models, SUPERUSER_ID, tools, _
from odoo.exceptions import AccessError, UserError, ValidationError
from odoo.modules.registry import Registry
from odoo.osv import expression
from odoo.tools.safe_eval import safe_eval
_logger = logging.getLogger(__name__)
@@ -469,6 +470,21 @@ class IrModelFields(models.Model):
msg = _("The field '%s' cannot be removed because the field '%s' depends on it.")
raise UserError(msg % (field, model._field_inverses[field][0]))
# remove fields from registry, and check that views are not broken
fields = [self.env[record.model]._pop_field(record.name) for record in self]
try:
domain = expression.OR([('arch_db', 'like', record.name)] for record in self)
views = self.env['ir.ui.view'].search(domain)
views._check_xml()
except Exception:
raise UserError("\n".join([
_("Cannot rename/delete fields that are still present in views:"),
", ".join(map(str, fields)),
]))
finally:
# the registry has been modified, restore it
self.pool.setup_models(self._cr)
@api.multi
def unlink(self):
if not self:
+42 -1
View File
@@ -1,7 +1,7 @@
# -*- coding: utf-8 -*-
# Part of Odoo. See LICENSE file for full copyright and licensing details.
from odoo.exceptions import ValidationError
from odoo.exceptions import UserError, ValidationError
from odoo.tools import mute_logger
import odoo.tests.common as common
import odoo.workflow
@@ -390,6 +390,9 @@ class TestCustomFields(common.TransactionCase):
super(TestCustomFields, self).setUp()
# do not reload the registry after removing a field
self.env = self.env(context={'_force_unlink': True})
def create_field(self, name):
""" create a custom field and return it """
model = self.env['ir.model'].search([('model', '=', self.MODEL)])
@@ -402,6 +405,14 @@ class TestCustomFields(common.TransactionCase):
self.assertIn(name, self.env[self.MODEL]._fields)
return field
def create_view(self, name):
""" create a view with the given field name """
return self.env['ir.ui.view'].create({
'name': 'yet another view',
'model': self.MODEL,
'arch': '<tree string="X"><field name="%s"/></tree>' % name,
})
def test_create_custom(self):
""" custom field names must be start with 'x_' """
with self.assertRaises(ValidationError):
@@ -436,3 +447,33 @@ class TestCustomFields(common.TransactionCase):
field2 = self.create_field('x_bar')
with self.assertRaises(ValidationError):
field2.name = field1.name
def test_remove_without_view(self):
""" try removing a custom field that does not occur in views """
field = self.create_field('x_foo')
field.unlink()
def test_rename_without_view(self):
""" try renaming a custom field that does not occur in views """
field = self.create_field('x_foo')
field.name = 'x_bar'
def test_remove_with_view(self):
""" try removing a custom field that occurs in a view """
field = self.create_field('x_foo')
self.create_view('x_foo')
# try to delete the field, this should fail but not modify the registry
with self.assertRaises(UserError):
field.unlink()
self.assertIn('x_foo', self.env[self.MODEL]._fields)
def test_rename_with_view(self):
""" try renaming a custom field that occurs in a view """
field = self.create_field('x_foo')
self.create_view('x_foo')
# try to delete the field, this should fail but not modify the registry
with self.assertRaises(UserError):
field.name = 'x_bar'
self.assertIn('x_foo', self.env[self.MODEL]._fields)