diff options
| author | Raphael Collet <rco@odoo.com> | 2019-09-04 11:19:11 +0000 |
|---|---|---|
| committer | Raphael Collet <rco@odoo.com> | 2019-09-10 13:29:15 +0000 |
| commit | 1315a1a82c0f09f4f97a04b26bedb590ceb8ecbb (patch) | |
| tree | fc206caa955d739cb5069782845181bd7c9a4562 | |
| parent | 3076ea8b1023d6b81a5db9674182598eb8cd63ac (diff) | |
[FIX] fields: clash with automatic fields and inheritance
Consider a model M that defines `display_name` as a computed stored
field, then an extension of M that introduces a mixin model A before
that definition in the MRO of M's class. The model A is expected to
have an automatic, non stored field `display_name`, while M must have
its non-automatic, stored field `display_name`.
closes odoo/odoo#36427
Signed-off-by: Raphael Collet (rco) <rco@openerp.com>
| -rw-r--r-- | odoo/addons/test_new_api/ir.model.access.csv | 1 | ||||
| -rw-r--r-- | odoo/addons/test_new_api/models.py | 25 | ||||
| -rw-r--r-- | odoo/addons/test_new_api/tests/test_new_fields.py | 38 | ||||
| -rw-r--r-- | odoo/fields.py | 8 |
4 files changed, 70 insertions, 2 deletions
diff --git a/odoo/addons/test_new_api/ir.model.access.csv b/odoo/addons/test_new_api/ir.model.access.csv index 3ec1388af3c..112b72e991d 100644 --- a/odoo/addons/test_new_api/ir.model.access.csv +++ b/odoo/addons/test_new_api/ir.model.access.csv @@ -40,3 +40,4 @@ access_test_new_api_model_b,access_test_new_api_model_b,model_test_new_api_model access_test_new_api_model_parent,access_test_new_api_model_parent,model_test_new_api_model_parent,,1,1,1,1 access_test_new_api_model_child,access_test_new_api_model_child,model_test_new_api_model_child,,1,1,1,1 access_test_new_api_model_child_nocheck,access_test_new_api_model_child_nocheck,model_test_new_api_model_child_nocheck,,1,1,1,1 +access_test_new_api_display,access_test_new_api_display,model_test_new_api_display,,1,1,1,1 diff --git a/odoo/addons/test_new_api/models.py b/odoo/addons/test_new_api/models.py index 79026a5cc13..c7188dec6cc 100644 --- a/odoo/addons/test_new_api/models.py +++ b/odoo/addons/test_new_api/models.py @@ -682,3 +682,28 @@ class ModelChildNoCheck(models.Model): name = fields.Char() company_id = fields.Many2one('res.company', required=True) parent_id = fields.Many2one('test_new_api.model_parent', check_company=False) + + +# model with explicit and stored field 'display_name' +class Display(models.Model): + _name = 'test_new_api.display' + _description = 'Model that overrides display_name' + + display_name = fields.Char(compute='_compute_display_name', store=True) + + def _compute_display_name(self): + for record in self: + record.display_name = 'My id is %s' % (record.id) + + +# abstract model with automatic and non-stored field 'display_name' +class Mixin(models.AbstractModel): + _name = 'test_new_api.mixin' + _description = 'Dummy mixin model' + + +# in this model extension, the field 'display_name' should not be inherited from +# 'test_new_api.mixin' +class ExtendedDisplay(models.Model): + _name = 'test_new_api.display' + _inherit = ['test_new_api.mixin', 'test_new_api.display'] diff --git a/odoo/addons/test_new_api/tests/test_new_fields.py b/odoo/addons/test_new_api/tests/test_new_fields.py index 03716624a0f..c7f83be715b 100644 --- a/odoo/addons/test_new_api/tests/test_new_fields.py +++ b/odoo/addons/test_new_api/tests/test_new_fields.py @@ -2,6 +2,7 @@ # test cases for new-style fields # import base64 +from collections import OrderedDict from datetime import date, datetime, time import io from PIL import Image @@ -1709,6 +1710,43 @@ class TestMagicFields(common.TransactionCase): self.assertEqual(record.create_uid, self.env.user) self.assertEqual(record.write_uid, self.env.user) + def test_mro_mixin(self): + # Mixin + # | + # | + # | + # ExtendedDisplay 'test_new_api.mixin' Display 'base' + # | | | | + # +----------------------+-+--------------+---------+ + # | + # 'test_new_api.display' + # + # The field 'display_name' is defined as store=True on the class Display + # above. The field 'display_name' on the model 'test_new_api.mixin' is + # expected to be automatic and non-stored. But the field 'display_name' + # on the model 'test_new_api.display' should not be automatic: it must + # correspond to the definition given in class Display, even if the MRO + # of the model shows the automatic field on the mixin model before the + # actual definition. + registry = self.env.registry + models = registry.models + + # check setup of models in alphanumeric order + self.patch(registry, 'models', OrderedDict(sorted(models.items()))) + registry.model_cache.clear() + registry.setup_models(self.cr) + field = registry['test_new_api.display'].display_name + self.assertFalse(field.automatic) + self.assertTrue(field.store) + + # check setup of models in reverse alphanumeric order + self.patch(registry, 'models', OrderedDict(sorted(models.items(), reverse=True))) + registry.model_cache.clear() + registry.setup_models(self.cr) + field = registry['test_new_api.display'].display_name + self.assertFalse(field.automatic) + self.assertTrue(field.store) + class TestParentStore(common.TransactionCase): diff --git a/odoo/fields.py b/odoo/fields.py index 0210eea5630..9e285517674 100644 --- a/odoo/fields.py +++ b/odoo/fields.py @@ -49,11 +49,12 @@ def first(records): def resolve_mro(model, name, predicate): """ Return the list of successively overridden values of attribute ``name`` - in mro order on ``model`` that satisfy ``predicate``. + in mro order on ``model`` that satisfy ``predicate``. Model classes + (the ones that appear in the registry) are ignored. """ result = [] for cls in type(model).__mro__: - if name in cls.__dict__: + if not getattr(cls, 'pool', None) and name in cls.__dict__: value = cls.__dict__[name] if not predicate(value): break @@ -392,6 +393,9 @@ class Field(MetaField('DummyField', (object,), {})): # determine all inherited field attributes modules = set() attrs = {} + if self.args.get('automatic') and resolve_mro(model, name, self._can_setup_from): + # prevent an automatic field from overriding a real field + self.args.clear() if not (self.args.get('automatic') or self.args.get('manual')): # magic and custom fields do not inherit from parent classes for field in reversed(resolve_mro(model, name, self._can_setup_from)): |
