[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.
This commit is contained in:
Raphael Collet 2014-07-10 22:04:03 +02:00
parent dc9bcf479d
commit e95fc488db
3 changed files with 45 additions and 5 deletions

View File

@ -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:

View File

@ -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:

View File

@ -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