From e95fc488dbc668932f0f8f8b2223d752c4745347 Mon Sep 17 00:00:00 2001 From: Raphael Collet Date: Thu, 10 Jul 2014 22:04:03 +0200 Subject: [PATCH] [IMP] fields: add mechanism to extend a selection field If a selection field is defined by a list as selection, such as: state = fields.Selection([('a', 'A'), ('b', 'B')]) one can extend it by inheritance by redefining the field, as: state = fields.Selection(selection_add=[('c', 'C')]) The result is that the selection field will have the list [('a', 'A'), ('b', 'B'), ('c', 'C')] as selection. --- openerp/addons/test_inherit/models.py | 11 +++++++++ .../addons/test_inherit/tests/test_inherit.py | 16 +++++++++---- openerp/fields.py | 23 ++++++++++++++++++- 3 files changed, 45 insertions(+), 5 deletions(-) diff --git a/openerp/addons/test_inherit/models.py b/openerp/addons/test_inherit/models.py index ebbe71e0521..f620dfe53a9 100644 --- a/openerp/addons/test_inherit/models.py +++ b/openerp/addons/test_inherit/models.py @@ -7,6 +7,7 @@ class mother(models.Model): name = fields.Char('Name', required=True) surname = fields.Char(compute='_compute_surname') + state = fields.Selection([('a', 'A'), ('b', 'B')]) @api.one @api.depends('name') @@ -35,6 +36,9 @@ class mother(models.Model): # extend the name field by adding a default value name = fields.Char(default='Unknown') + # extend the selection of the state field + state = fields.Selection(selection_add=[('c', 'C')]) + # override the computed field, and extend its dependencies @api.one @api.depends('field_in_mother') @@ -44,4 +48,11 @@ class mother(models.Model): else: super(mother, self)._compute_surname() + +class mother(models.Model): + _inherit = 'test.inherit.mother' + + # extend again the selection of the state field + state = fields.Selection(selection_add=[('d', 'D')]) + # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/openerp/addons/test_inherit/tests/test_inherit.py b/openerp/addons/test_inherit/tests/test_inherit.py index b663d911f60..8039e698669 100644 --- a/openerp/addons/test_inherit/tests/test_inherit.py +++ b/openerp/addons/test_inherit/tests/test_inherit.py @@ -9,15 +9,15 @@ class test_inherits(common.TransactionCase): # is accessible from the child model. This test has been written # to verify the purpose of the inheritance computing of the class # in the openerp.osv.orm._build_model. - mother = self.registry('test.inherit.mother') - daugther = self.registry('test.inherit.daugther') + mother = self.env['test.inherit.mother'] + daugther = self.env['test.inherit.daugther'] self.assertIn('field_in_mother', mother._fields) self.assertIn('field_in_mother', daugther._fields) def test_field_extension(self): """ check the extension of a field in an inherited model """ - mother = self.registry('test.inherit.mother') + mother = self.env['test.inherit.mother'] field = mother._fields['name'] # the field should inherit required=True, and have a default value @@ -26,11 +26,19 @@ class test_inherits(common.TransactionCase): def test_depends_extension(self): """ check that @depends on overridden compute methods extends dependencies """ - mother = self.registry('test.inherit.mother') + mother = self.env['test.inherit.mother'] field = mother._fields['surname'] # the field dependencies are added self.assertItemsEqual(field.depends, ['name', 'field_in_mother']) + def test_selection_extension(self): + """ check that attribute selection_add=... extends selection on fields. """ + mother = self.env['test.inherit.mother'] + field = mother._fields['state'] + + # the extra values are added + self.assertEqual(field.selection, [('a', 'A'), ('b', 'B'), ('c', 'C'), ('d', 'D')]) + # vim:expandtab:smartindent:tabstop=4:softtabstop=4:shiftwidth=4: diff --git a/openerp/fields.py b/openerp/fields.py index bf6e29826b5..71ded7c4a5e 100644 --- a/openerp/fields.py +++ b/openerp/fields.py @@ -1116,12 +1116,16 @@ class Selection(Field): It is given as either a list of pairs (`value`, `string`), or a model method, or a method name. + :param selection_add: provides an extension of the selection in the case + of an overridden field. It is a list of pairs (`value`, `string`). + The attribute `selection` is mandatory except in the case of related fields (see :ref:`field-related`) or field extensions (see :ref:`field-incremental-definition`). """ type = 'selection' - selection = None # [(value, string), ...], model method or method name + selection = None # [(value, string), ...], function or method name + selection_add = None # [(value, string), ...] def __init__(self, selection=None, string=None, **kwargs): if callable(selection): @@ -1135,6 +1139,23 @@ class Selection(Field): field = self.related_field self.selection = lambda model: field._description_selection(model.env) + def _setup_regular(self, env): + super(Selection, self)._setup_regular(env) + # determine selection (applying extensions) + cls = type(env[self.model_name]) + selection = None + for field in resolve_all_mro(cls, self.name, reverse=True): + if isinstance(field, type(self)): + # We cannot use field.selection or field.selection_add here + # because those attributes are overridden by `set_class_name`. + if 'selection' in field._attrs: + selection = field._attrs['selection'] + if 'selection_add' in field._attrs: + selection = selection + field._attrs['selection_add'] + else: + selection = None + self.selection = selection + def _description_selection(self, env): """ return the selection list (pairs (value, label)); labels are translated according to context language