[IMP] models: do not use compute methods to determine default values anymore
Compute methods could give results that should not be considered as default values. For instance, a related field usually defaults to a null value, which is then set to the field with its inverse method by create(). This may violate a non-null constraint if the original field is required. Therefore, compute methods are no longer used to determine default values.
This commit is contained in:
parent
044ed06fec
commit
5db84cb07e
|
@ -335,7 +335,7 @@ class ir_values(osv.osv):
|
||||||
(row['id'], row['name'], pickle.loads(row['value'].encode('utf-8'))))
|
(row['id'], row['name'], pickle.loads(row['value'].encode('utf-8'))))
|
||||||
return defaults.values()
|
return defaults.values()
|
||||||
|
|
||||||
# use ormcache: this is called a lot by BaseModel.add_default_value()!
|
# use ormcache: this is called a lot by BaseModel.default_get()!
|
||||||
@tools.ormcache(skiparg=2)
|
@tools.ormcache(skiparg=2)
|
||||||
def get_defaults_dict(self, cr, uid, model, condition=False):
|
def get_defaults_dict(self, cr, uid, model, condition=False):
|
||||||
""" Returns a dictionary mapping field names with their corresponding
|
""" Returns a dictionary mapping field names with their corresponding
|
||||||
|
|
|
@ -117,7 +117,7 @@ class Message(models.Model):
|
||||||
@api.one
|
@api.one
|
||||||
@api.depends('author.name', 'discussion.name')
|
@api.depends('author.name', 'discussion.name')
|
||||||
def _compute_name(self):
|
def _compute_name(self):
|
||||||
self.name = "[%s] %s" % (self.discussion.name or '', self.author.name)
|
self.name = "[%s] %s" % (self.discussion.name or '', self.author.name or '')
|
||||||
|
|
||||||
@api.one
|
@api.one
|
||||||
@api.depends('author.name', 'discussion.name', 'body')
|
@api.depends('author.name', 'discussion.name', 'body')
|
||||||
|
|
|
@ -349,20 +349,18 @@ class TestNewFields(common.TransactionCase):
|
||||||
message.body = BODY = "May the Force be with you."
|
message.body = BODY = "May the Force be with you."
|
||||||
self.assertEqual(message.discussion, discussion)
|
self.assertEqual(message.discussion, discussion)
|
||||||
self.assertEqual(message.body, BODY)
|
self.assertEqual(message.body, BODY)
|
||||||
|
self.assertFalse(message.author)
|
||||||
self.assertNotIn(message, discussion.messages)
|
self.assertNotIn(message, discussion.messages)
|
||||||
|
|
||||||
# check computed values of fields
|
# check computed values of fields
|
||||||
user = self.env.user
|
self.assertEqual(message.name, "[%s] %s" % (discussion.name, ''))
|
||||||
self.assertEqual(message.author, user)
|
|
||||||
self.assertEqual(message.name, "[%s] %s" % (discussion.name, user.name))
|
|
||||||
self.assertEqual(message.size, len(BODY))
|
self.assertEqual(message.size, len(BODY))
|
||||||
|
|
||||||
def test_41_defaults(self):
|
def test_41_defaults(self):
|
||||||
""" test default values. """
|
""" test default values. """
|
||||||
fields = ['discussion', 'body', 'author', 'size']
|
fields = ['discussion', 'body', 'author', 'size']
|
||||||
defaults = self.env['test_new_api.message'].default_get(fields)
|
defaults = self.env['test_new_api.message'].default_get(fields)
|
||||||
self.assertEqual(defaults, {'author': self.env.uid, 'size': 0})
|
self.assertEqual(defaults, {'author': self.env.uid})
|
||||||
|
|
||||||
defaults = self.env['test_new_api.mixed'].default_get(['number'])
|
defaults = self.env['test_new_api.mixed'].default_get(['number'])
|
||||||
self.assertEqual(defaults, {'number': 3.14})
|
self.assertEqual(defaults, {'number': 3.14})
|
||||||
|
|
|
@ -753,8 +753,8 @@ class Field(object):
|
||||||
# normal record -> read or compute value for this field
|
# normal record -> read or compute value for this field
|
||||||
self.determine_value(record)
|
self.determine_value(record)
|
||||||
else:
|
else:
|
||||||
# new record -> compute default value for this field
|
# draft record -> compute the value or let it be null
|
||||||
record.add_default_value(self)
|
self.determine_draft_value(record)
|
||||||
|
|
||||||
# the result should be in cache now
|
# the result should be in cache now
|
||||||
return record._cache[self]
|
return record._cache[self]
|
||||||
|
@ -861,12 +861,9 @@ class Field(object):
|
||||||
# this is a non-stored non-computed field
|
# this is a non-stored non-computed field
|
||||||
record._cache[self] = self.null(env)
|
record._cache[self] = self.null(env)
|
||||||
|
|
||||||
def determine_default(self, record):
|
def determine_draft_value(self, record):
|
||||||
""" determine the default value of field `self` on `record` """
|
""" Determine the value of `self` for the given draft `record`. """
|
||||||
if self.default:
|
if self.compute:
|
||||||
value = self.default(record)
|
|
||||||
record._cache[self] = self.convert_to_cache(value, record)
|
|
||||||
elif self.compute:
|
|
||||||
self._compute_value(record)
|
self._compute_value(record)
|
||||||
else:
|
else:
|
||||||
record._cache[self] = SpecialValue(self.null(record.env))
|
record._cache[self] = SpecialValue(self.null(record.env))
|
||||||
|
@ -1470,15 +1467,6 @@ class Many2one(_Relational):
|
||||||
def convert_to_display_name(self, value):
|
def convert_to_display_name(self, value):
|
||||||
return ustr(value.display_name)
|
return ustr(value.display_name)
|
||||||
|
|
||||||
def determine_default(self, record):
|
|
||||||
super(Many2one, self).determine_default(record)
|
|
||||||
if self.delegate:
|
|
||||||
# special case: fields that implement inheritance between models
|
|
||||||
value = record[self.name]
|
|
||||||
if not value:
|
|
||||||
# the default value cannot be null, use a new record instead
|
|
||||||
record[self.name] = record.env[self.comodel_name].new()
|
|
||||||
|
|
||||||
|
|
||||||
class UnionUpdate(SpecialValue):
|
class UnionUpdate(SpecialValue):
|
||||||
""" Placeholder for a value update; when this value is taken from the cache,
|
""" Placeholder for a value update; when this value is taken from the cache,
|
||||||
|
|
|
@ -1305,7 +1305,8 @@ class BaseModel(object):
|
||||||
except Exception, e:
|
except Exception, e:
|
||||||
raise ValidationError("Error while validating constraint\n\n%s" % tools.ustr(e))
|
raise ValidationError("Error while validating constraint\n\n%s" % tools.ustr(e))
|
||||||
|
|
||||||
def default_get(self, cr, uid, fields_list, context=None):
|
@api.model
|
||||||
|
def default_get(self, fields_list):
|
||||||
""" default_get(fields) -> default_values
|
""" default_get(fields) -> default_values
|
||||||
|
|
||||||
Return default values for the fields in `fields_list`. Default
|
Return default values for the fields in `fields_list`. Default
|
||||||
|
@ -1314,60 +1315,56 @@ class BaseModel(object):
|
||||||
|
|
||||||
:param fields_list: a list of field names
|
:param fields_list: a list of field names
|
||||||
:return: a dictionary mapping each field name to its corresponding
|
:return: a dictionary mapping each field name to its corresponding
|
||||||
default value; the keys of the dictionary are the fields in
|
default value, if it has one.
|
||||||
`fields_list` that have a default value different from ``False``.
|
|
||||||
|
|
||||||
This method should not be overridden. In order to change the
|
|
||||||
mechanism for determining default values, you should override method
|
|
||||||
:meth:`add_default_value` instead.
|
|
||||||
"""
|
"""
|
||||||
# trigger view init hook
|
# trigger view init hook
|
||||||
self.view_init(cr, uid, fields_list, context)
|
self.view_init(fields_list)
|
||||||
|
|
||||||
|
defaults = {}
|
||||||
|
parent_fields = defaultdict(list)
|
||||||
|
|
||||||
# use a new record to determine default values; evaluate fields on the
|
|
||||||
# new record and put default values in result
|
|
||||||
record = self.new(cr, uid, {}, context=context)
|
|
||||||
result = {}
|
|
||||||
for name in fields_list:
|
for name in fields_list:
|
||||||
if name in self._fields:
|
# 1. look up context
|
||||||
value = record[name]
|
key = 'default_' + name
|
||||||
if name in record._cache:
|
if key in self._context:
|
||||||
result[name] = value # it really is a default value
|
defaults[name] = self._context[key]
|
||||||
|
continue
|
||||||
|
|
||||||
# convert default values to the expected format
|
# 2. look up ir_values
|
||||||
result = self._convert_to_write(result)
|
# Note: performance is good, because get_defaults_dict is cached!
|
||||||
return result
|
ir_values_dict = self.env['ir.values'].get_defaults_dict(self._name)
|
||||||
|
if name in ir_values_dict:
|
||||||
|
defaults[name] = ir_values_dict[name]
|
||||||
|
continue
|
||||||
|
|
||||||
def add_default_value(self, field):
|
field = self._fields.get(name)
|
||||||
""" Set the default value of `field` to the new record `self`.
|
|
||||||
The value must be assigned to `self`.
|
|
||||||
"""
|
|
||||||
assert not self.id, "Expected new record: %s" % self
|
|
||||||
cr, uid, context = self.env.args
|
|
||||||
name = field.name
|
|
||||||
|
|
||||||
# 1. look up context
|
# 3. look up property fields
|
||||||
key = 'default_' + name
|
# TODO: get rid of this one
|
||||||
if key in context:
|
if field and field.company_dependent:
|
||||||
self[name] = context[key]
|
defaults[name] = self.env['ir.property'].get(name, self._name)
|
||||||
return
|
continue
|
||||||
|
|
||||||
# 2. look up ir_values
|
# 4. look up field.default
|
||||||
# Note: performance is good, because get_defaults_dict is cached!
|
if field and field.default:
|
||||||
ir_values_dict = self.env['ir.values'].get_defaults_dict(self._name)
|
defaults[name] = field.default(self)
|
||||||
if name in ir_values_dict:
|
continue
|
||||||
self[name] = ir_values_dict[name]
|
|
||||||
return
|
|
||||||
|
|
||||||
# 3. look up property fields
|
# 5. delegate to parent model
|
||||||
# TODO: get rid of this one
|
if field and field.inherited:
|
||||||
column = self._columns.get(name)
|
field = field.related_field
|
||||||
if isinstance(column, fields.property):
|
parent_fields[field.model_name].append(field.name)
|
||||||
self[name] = self.env['ir.property'].get(name, self._name)
|
|
||||||
return
|
|
||||||
|
|
||||||
# 4. delegate to field
|
# convert default values to the right format
|
||||||
field.determine_default(self)
|
defaults = self._convert_to_cache(defaults, validate=False)
|
||||||
|
defaults = self._convert_to_write(defaults)
|
||||||
|
|
||||||
|
# add default values for inherited fields
|
||||||
|
for model, names in parent_fields.iteritems():
|
||||||
|
defaults.update(self.env[model].default_get(names))
|
||||||
|
|
||||||
|
return defaults
|
||||||
|
|
||||||
def fields_get_keys(self, cr, user, context=None):
|
def fields_get_keys(self, cr, user, context=None):
|
||||||
res = self._columns.keys()
|
res = self._columns.keys()
|
||||||
|
@ -5260,7 +5257,7 @@ class BaseModel(object):
|
||||||
|
|
||||||
#
|
#
|
||||||
# New records - represent records that do not exist in the database yet;
|
# New records - represent records that do not exist in the database yet;
|
||||||
# they are used to compute default values and perform onchanges.
|
# they are used to perform onchanges.
|
||||||
#
|
#
|
||||||
|
|
||||||
@api.model
|
@api.model
|
||||||
|
|
Loading…
Reference in New Issue