[FIX] ir_model: prevent renaming/removing fields that are used in views
This commit is contained in:
@@ -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
|
||||
|
||||
@@ -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:
|
||||
|
||||
@@ -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)
|
||||
|
||||
Reference in New Issue
Block a user