[IMP] fields: set the default value to the closest field.default or _defaults
This solves a subtle issue: in the following case, the class Bar should override the default value set by Foo. But in practice it was not working, because _defaults is looked up before field.default. class Foo(models.Model): _name = 'foo' _columns = { 'foo': fields.char('Foo'), } _defaults = { 'foo': "Foo", } class Bar(models.Model): _inherit = 'foo' foo = fields.Char(default="Bar") The change makes field.default and the model's _defaults consistent with each other.
This commit is contained in:
parent
3f31081bc2
commit
36174fcc6e
|
@ -7,9 +7,12 @@ class mother(models.Model):
|
|||
|
||||
_columns = {
|
||||
# check interoperability of field inheritance with old-style fields
|
||||
'name': osv.fields.char('Name', required=True),
|
||||
'name': osv.fields.char('Name'),
|
||||
'state': osv.fields.selection([('a', 'A'), ('b', 'B')], string='State'),
|
||||
}
|
||||
_defaults = {
|
||||
'name': 'Foo',
|
||||
}
|
||||
|
||||
surname = fields.Char(compute='_compute_surname')
|
||||
|
||||
|
@ -37,8 +40,8 @@ class mother(models.Model):
|
|||
|
||||
field_in_mother = fields.Char()
|
||||
|
||||
# extend the name field by adding a default value
|
||||
name = fields.Char(default='Unknown')
|
||||
# extend the name field: make it required and change its default value
|
||||
name = fields.Char(required=True, default='Bar')
|
||||
|
||||
# extend the selection of the state field
|
||||
state = fields.Selection(selection_add=[('c', 'C')])
|
||||
|
|
|
@ -17,12 +17,15 @@ class test_inherits(common.TransactionCase):
|
|||
|
||||
def test_field_extension(self):
|
||||
""" check the extension of a field in an inherited model """
|
||||
# the field mother.name should inherit required=True, and have a default
|
||||
# value
|
||||
# the field mother.name should inherit required=True, and have "Bar" as
|
||||
# a default value
|
||||
mother = self.env['test.inherit.mother']
|
||||
field = mother._fields['name']
|
||||
self.assertTrue(field.required)
|
||||
self.assertEqual(field.default(mother), 'Unknown')
|
||||
|
||||
self.assertEqual(field.default(mother), "Bar")
|
||||
self.assertEqual(mother.default_get(['name']), {'name': "Bar"})
|
||||
self.assertEqual(mother._defaults.get('name'), "Bar")
|
||||
|
||||
# the field daugther.template_id should inherit
|
||||
# model_name='test.inherit.mother', string='Template', required=True
|
||||
|
|
|
@ -312,10 +312,6 @@ class Field(object):
|
|||
attrs.update(self._attrs) # necessary in case self is not in cls
|
||||
|
||||
# initialize `self` with `attrs`
|
||||
if 'default' in attrs and not callable(attrs['default']):
|
||||
# make default callable
|
||||
value = attrs['default']
|
||||
attrs['default'] = lambda recs: value
|
||||
if attrs.get('compute'):
|
||||
# by default, computed fields are not stored, not copied and readonly
|
||||
attrs['store'] = attrs.get('store', False)
|
||||
|
@ -336,8 +332,48 @@ class Field(object):
|
|||
if not self.string:
|
||||
self.string = name.replace('_', ' ').capitalize()
|
||||
|
||||
# determine self.default and cls._defaults in a consistent way
|
||||
self._determine_default(cls, name)
|
||||
|
||||
self.reset()
|
||||
|
||||
def _determine_default(self, cls, name):
|
||||
""" Retrieve the default value for `self` in the hierarchy of `cls`, and
|
||||
determine `self.default` and `cls._defaults` accordingly.
|
||||
"""
|
||||
self.default = None
|
||||
|
||||
# traverse the class hierarchy upwards, and take the first field
|
||||
# definition with a default or _defaults for self
|
||||
for klass in cls.__mro__:
|
||||
field = klass.__dict__.get(name, self)
|
||||
if not isinstance(field, type(self)):
|
||||
return # klass contains another value overridden by self
|
||||
|
||||
if 'default' in field._attrs:
|
||||
# take the default in field, and adapt it for cls._defaults
|
||||
value = field._attrs['default']
|
||||
if callable(value):
|
||||
self.default = value
|
||||
cls._defaults[name] = lambda model, cr, uid, context: \
|
||||
self.convert_to_write(value(model.browse(cr, uid, [], context)))
|
||||
else:
|
||||
self.default = lambda recs: value
|
||||
cls._defaults[name] = value
|
||||
return
|
||||
|
||||
defaults = klass.__dict__.get('_defaults') or {}
|
||||
if name in defaults:
|
||||
# take the value from _defaults, and adapt it for self.default
|
||||
value = defaults[name]
|
||||
value_func = value if callable(value) else lambda *args: value
|
||||
self.default = lambda recs: self.convert_to_cache(
|
||||
value_func(recs._model, recs._cr, recs._uid, recs._context),
|
||||
recs, validate=False,
|
||||
)
|
||||
cls._defaults[name] = value
|
||||
return
|
||||
|
||||
def __str__(self):
|
||||
return "%s.%s" % (self.model_name, self.name)
|
||||
|
||||
|
|
|
@ -238,8 +238,9 @@ class MetaModel(api.Meta):
|
|||
|
||||
# transform columns into new-style fields (enables field inheritance)
|
||||
for name, column in self._columns.iteritems():
|
||||
if not hasattr(self, name):
|
||||
setattr(self, name, column.to_field())
|
||||
if name in self.__dict__:
|
||||
_logger.warning("Field %r erasing an existing value", name)
|
||||
setattr(self, name, column.to_field())
|
||||
|
||||
|
||||
class NewId(object):
|
||||
|
@ -602,9 +603,6 @@ class BaseModel(object):
|
|||
)
|
||||
columns.update(cls._columns)
|
||||
|
||||
defaults = dict(parent_class._defaults)
|
||||
defaults.update(cls._defaults)
|
||||
|
||||
inherits = dict(parent_class._inherits)
|
||||
inherits.update(cls._inherits)
|
||||
|
||||
|
@ -629,7 +627,6 @@ class BaseModel(object):
|
|||
'_name': name,
|
||||
'_register': False,
|
||||
'_columns': columns,
|
||||
'_defaults': defaults,
|
||||
'_inherits': inherits,
|
||||
'_depends': depends,
|
||||
'_constraints': constraints,
|
||||
|
@ -643,7 +640,7 @@ class BaseModel(object):
|
|||
'_name': name,
|
||||
'_register': False,
|
||||
'_columns': dict(cls._columns),
|
||||
'_defaults': dict(cls._defaults),
|
||||
'_defaults': {}, # filled by Field._determine_default()
|
||||
'_inherits': dict(cls._inherits),
|
||||
'_depends': dict(cls._depends),
|
||||
'_constraints': list(cls._constraints),
|
||||
|
@ -1369,15 +1366,7 @@ class BaseModel(object):
|
|||
self[name] = self.env['ir.property'].get(name, self._name)
|
||||
return
|
||||
|
||||
# 4. look up _defaults
|
||||
if name in self._defaults:
|
||||
value = self._defaults[name]
|
||||
if callable(value):
|
||||
value = value(self._model, cr, uid, context)
|
||||
self[name] = value
|
||||
return
|
||||
|
||||
# 5. delegate to field
|
||||
# 4. delegate to field
|
||||
field.determine_default(self)
|
||||
|
||||
def fields_get_keys(self, cr, user, context=None):
|
||||
|
@ -2413,23 +2402,14 @@ class BaseModel(object):
|
|||
|
||||
|
||||
def _set_default_value_on_column(self, cr, column_name, context=None):
|
||||
# ideally should use add_default_value but fails
|
||||
# due to ir.values not being ready
|
||||
# ideally, we should use default_get(), but it fails due to ir.values
|
||||
# not being ready
|
||||
|
||||
# get old-style default
|
||||
# get default value
|
||||
default = self._defaults.get(column_name)
|
||||
if callable(default):
|
||||
default = default(self, cr, SUPERUSER_ID, context)
|
||||
|
||||
# get new_style default if no old-style
|
||||
if default is None:
|
||||
record = self.new(cr, SUPERUSER_ID, context=context)
|
||||
field = self._fields[column_name]
|
||||
field.determine_default(record)
|
||||
defaults = dict(record._cache)
|
||||
if column_name in defaults:
|
||||
default = field.convert_to_write(defaults[column_name])
|
||||
|
||||
column = self._columns[column_name]
|
||||
ss = column._symbol_set
|
||||
db_default = ss[1](default)
|
||||
|
|
Loading…
Reference in New Issue