[REF] fields: change API and semantics of converter methods
The cache data format has been split between a cache format and a record format
(returned by accessing a field on a record). We have the following converters:
ANY FORMAT
|
| convert_to_cache
v
CACHE FORMAT
|
| convert_to_record
v
RECORD FORMAT
|
| convert_to_read
| convert_to_write
| convert_to_onchange
| convert_to_export
| convert_to_display_name
v
OTHER FORMATS
Modify the API of the converter methods to make them uniform (pass value and
record), and adapt their documentation.
This commit is contained in:
@@ -41,7 +41,6 @@ class SaleOrderEventRegistration(models.TransientModel):
|
||||
'sale_order_line_id': so_line.id,
|
||||
}])
|
||||
res['event_registration_ids'] = attendee_list
|
||||
res = self._convert_to_cache(res, validate=False)
|
||||
res = self._convert_to_write(res)
|
||||
return res
|
||||
|
||||
|
||||
@@ -398,7 +398,7 @@ class MailComposer(models.TransientModel):
|
||||
# ORM handle the assignation of command list on new onchange (api.v8),
|
||||
# this force the complete replacement of x2many field with
|
||||
# command and is compatible with onchange api.v7
|
||||
values = self._convert_to_write(self._convert_to_cache(values))
|
||||
values = self._convert_to_write(values)
|
||||
|
||||
return {'value': values}
|
||||
|
||||
|
||||
@@ -402,7 +402,7 @@ class IrValues(models.Model):
|
||||
# FIXME: needs cleanup
|
||||
try:
|
||||
action_def = {
|
||||
field: action._fields[field].convert_to_read(action[field])
|
||||
field: action._fields[field].convert_to_read(action[field], action)
|
||||
for field in fields
|
||||
}
|
||||
if action._name in ('ir.actions.report.xml', 'ir.actions.act_window'):
|
||||
|
||||
+84
-85
@@ -81,7 +81,7 @@ def default_new_to_old(field, value):
|
||||
""" Convert the new-API default ``value`` to the old API. """
|
||||
if callable(value):
|
||||
from openerp import api
|
||||
return api.model(lambda model: field.convert_to_write(value(model)))
|
||||
return api.model(lambda model: field.convert_to_write(value(model), model))
|
||||
else:
|
||||
return value
|
||||
|
||||
@@ -758,57 +758,56 @@ class Field(object):
|
||||
# Conversion of values
|
||||
#
|
||||
|
||||
def null(self, env):
|
||||
""" return the null value for this field in the given environment """
|
||||
def null(self, record):
|
||||
""" Return the null value for this field in the record format. """
|
||||
return False
|
||||
|
||||
def convert_to_cache(self, value, record, validate=True):
|
||||
""" convert ``value`` to the cache level in ``env``; ``value`` may come from
|
||||
an assignment, or have the format of methods :meth:`BaseModel.read`
|
||||
or :meth:`BaseModel.write`
|
||||
""" Convert ``value`` to the cache format; ``value`` may come from an
|
||||
assignment, or have the format of methods :meth:`BaseModel.read` or
|
||||
:meth:`BaseModel.write`.
|
||||
|
||||
:param record: the target record for the assignment, or an empty recordset
|
||||
|
||||
:param bool validate: when True, field-specific validation of
|
||||
``value`` will be performed
|
||||
:param bool validate: when True, field-specific validation of ``value``
|
||||
will be performed
|
||||
"""
|
||||
return value
|
||||
|
||||
def convert_to_read(self, value, use_name_get=True):
|
||||
""" convert ``value`` from the cache to a value as returned by method
|
||||
:meth:`BaseModel.read`
|
||||
def convert_to_record(self, value, record):
|
||||
""" Convert ``value`` from the cache format to the record format. """
|
||||
return value
|
||||
|
||||
:param bool use_name_get: when True, value's diplay name will
|
||||
be computed using :meth:`BaseModel.name_get`, if relevant
|
||||
for the field
|
||||
def convert_to_read(self, value, record, use_name_get=True):
|
||||
""" Convert ``value`` from the record format to the format returned by
|
||||
method :meth:`BaseModel.read`.
|
||||
|
||||
:param bool use_name_get: when True, the value's display name will be
|
||||
computed using :meth:`BaseModel.name_get`, if relevant for the field
|
||||
"""
|
||||
return False if value is None else value
|
||||
|
||||
def convert_to_write(self, value):
|
||||
""" convert ``value`` from the cache to a valid value for method
|
||||
:meth:`BaseModel.write`.
|
||||
def convert_to_write(self, value, record):
|
||||
""" Convert ``value`` from the record format to the format of method
|
||||
:meth:`BaseModel.write`.
|
||||
"""
|
||||
return self.convert_to_read(value)
|
||||
return self.convert_to_read(value, record)
|
||||
|
||||
def convert_to_onchange(self, value, fnames=None):
|
||||
""" convert ``value`` from the cache to a value as returned by method
|
||||
:meth:`BaseModel.onchange`.
|
||||
def convert_to_onchange(self, value, record, fnames=()):
|
||||
""" Convert ``value`` from the record format to the format returned by
|
||||
method :meth:`BaseModel.onchange`.
|
||||
|
||||
:param fnames: an optional collection of field names to convert
|
||||
(for relational fields only)
|
||||
:param fnames: an optional collection of field names to convert
|
||||
(for relational fields only)
|
||||
"""
|
||||
return self.convert_to_read(value)
|
||||
return self.convert_to_read(value, record)
|
||||
|
||||
def convert_to_export(self, value, env):
|
||||
""" convert ``value`` from the cache to a valid value for export. The
|
||||
parameter ``env`` is given for managing translations.
|
||||
"""
|
||||
def convert_to_export(self, value, record):
|
||||
""" Convert ``value`` from the record format to the export format. """
|
||||
if not value:
|
||||
return ''
|
||||
return value if env.context.get('export_raw_data') else ustr(value)
|
||||
return value if record._context.get('export_raw_data') else ustr(value)
|
||||
|
||||
def convert_to_display_name(self, value, record=None):
|
||||
""" convert ``value`` from the cache to a suitable display name. """
|
||||
def convert_to_display_name(self, value, record):
|
||||
""" Convert ``value`` from the record format to a suitable display name. """
|
||||
return ustr(value)
|
||||
|
||||
############################################################################
|
||||
@@ -823,26 +822,22 @@ class Field(object):
|
||||
|
||||
if not record:
|
||||
# null record -> return the null value for this field
|
||||
return self.null(record.env)
|
||||
return self.null(record)
|
||||
|
||||
# only a single record may be accessed
|
||||
record.ensure_one()
|
||||
|
||||
try:
|
||||
return record._cache[self]
|
||||
value = record._cache[self]
|
||||
except KeyError:
|
||||
pass
|
||||
# cache miss, determine value and retrieve it
|
||||
if record.id:
|
||||
self.determine_value(record)
|
||||
else:
|
||||
self.determine_draft_value(record)
|
||||
value = record._cache[self]
|
||||
|
||||
# cache miss, retrieve value
|
||||
if record.id:
|
||||
# normal record -> read or compute value for this field
|
||||
self.determine_value(record)
|
||||
else:
|
||||
# draft record -> compute the value or let it be null
|
||||
self.determine_draft_value(record)
|
||||
|
||||
# the result should be in cache now
|
||||
return record._cache[self]
|
||||
return self.convert_to_record(value, record)
|
||||
|
||||
def __set__(self, record, value):
|
||||
""" set the value of field ``self`` on ``record`` """
|
||||
@@ -872,7 +867,8 @@ class Field(object):
|
||||
|
||||
else:
|
||||
# simply write to the database, and update cache
|
||||
record.write({self.name: self.convert_to_write(value)})
|
||||
write_value = self.convert_to_write(self.convert_to_record(value, record), record)
|
||||
record.write({self.name: write_value})
|
||||
record._cache[self] = value
|
||||
|
||||
############################################################################
|
||||
@@ -885,7 +881,8 @@ class Field(object):
|
||||
# initialize the fields to their corresponding null value in cache
|
||||
computed = records._field_computed[self]
|
||||
for field in computed:
|
||||
records._cache[field] = field.null(records.env)
|
||||
for record in records:
|
||||
record._cache[field] = field.convert_to_cache(False, record, validate=False)
|
||||
records.env.computed[field].update(records._ids)
|
||||
if isinstance(self.compute, basestring):
|
||||
getattr(records, self.compute)()
|
||||
@@ -947,14 +944,15 @@ class Field(object):
|
||||
|
||||
else:
|
||||
# this is a non-stored non-computed field
|
||||
record._cache[self] = self.null(env)
|
||||
record._cache[self] = self.convert_to_cache(False, record, validate=False)
|
||||
|
||||
def determine_draft_value(self, record):
|
||||
""" Determine the value of ``self`` for the given draft ``record``. """
|
||||
if self.compute:
|
||||
self._compute_value(record)
|
||||
else:
|
||||
record._cache[self] = SpecialValue(self.null(record.env))
|
||||
null = self.convert_to_cache(False, record, validate=False)
|
||||
record._cache[self] = SpecialValue(null)
|
||||
|
||||
def determine_inverse(self, records):
|
||||
""" Given the value of ``self`` on ``records``, inverse the computation. """
|
||||
@@ -1047,8 +1045,8 @@ class Boolean(Field):
|
||||
def convert_to_cache(self, value, record, validate=True):
|
||||
return bool(value)
|
||||
|
||||
def convert_to_export(self, value, env):
|
||||
if env.context.get('export_raw_data'):
|
||||
def convert_to_export(self, value, record):
|
||||
if record._context.get('export_raw_data'):
|
||||
return value
|
||||
return ustr(value)
|
||||
|
||||
@@ -1068,7 +1066,7 @@ class Integer(Field):
|
||||
return value.get('id', False)
|
||||
return int(value or 0)
|
||||
|
||||
def convert_to_read(self, value, use_name_get=True):
|
||||
def convert_to_read(self, value, record, use_name_get=True):
|
||||
# Integer values greater than 2^31-1 are not supported in pure XMLRPC,
|
||||
# so we have to pass them as floats :-(
|
||||
if value and value > xmlrpclib.MAXINT:
|
||||
@@ -1079,9 +1077,9 @@ class Integer(Field):
|
||||
# special case, when an integer field is used as inverse for a one2many
|
||||
records._cache[self] = value.id or 0
|
||||
|
||||
def convert_to_export(self, value, env):
|
||||
def convert_to_export(self, value, record):
|
||||
if value or value == 0:
|
||||
return value if env.context.get('export_raw_data') else ustr(value)
|
||||
return value if record._context.get('export_raw_data') else ustr(value)
|
||||
return ''
|
||||
|
||||
|
||||
@@ -1125,9 +1123,9 @@ class Float(Field):
|
||||
digits = self.digits
|
||||
return float_round(value, precision_digits=digits[1]) if digits else value
|
||||
|
||||
def convert_to_export(self, value, env):
|
||||
def convert_to_export(self, value, record):
|
||||
if value or value == 0.0:
|
||||
return value if env.context.get('export_raw_data') else ustr(value)
|
||||
return value if record._context.get('export_raw_data') else ustr(value)
|
||||
return ''
|
||||
|
||||
|
||||
@@ -1365,10 +1363,10 @@ class Date(Field):
|
||||
return value[:DATE_LENGTH]
|
||||
return self.to_string(value)
|
||||
|
||||
def convert_to_export(self, value, env):
|
||||
def convert_to_export(self, value, record):
|
||||
if not value:
|
||||
return ''
|
||||
return self.from_string(value) if env.context.get('export_raw_data') else ustr(value)
|
||||
return self.from_string(value) if record._context.get('export_raw_data') else ustr(value)
|
||||
|
||||
|
||||
class Datetime(Field):
|
||||
@@ -1436,12 +1434,12 @@ class Datetime(Field):
|
||||
return value
|
||||
return self.to_string(value)
|
||||
|
||||
def convert_to_export(self, value, env):
|
||||
def convert_to_export(self, value, record):
|
||||
if not value:
|
||||
return ''
|
||||
return self.from_string(value) if env.context.get('export_raw_data') else ustr(value)
|
||||
return self.from_string(value) if record._context.get('export_raw_data') else ustr(value)
|
||||
|
||||
def convert_to_display_name(self, value, record=None):
|
||||
def convert_to_display_name(self, value, record):
|
||||
assert record, 'Record expected'
|
||||
return Datetime.to_string(Datetime.context_timestamp(record, Datetime.from_string(value)))
|
||||
|
||||
@@ -1547,11 +1545,11 @@ class Selection(Field):
|
||||
return False
|
||||
raise ValueError("Wrong value for %s: %r" % (self, value))
|
||||
|
||||
def convert_to_export(self, value, env):
|
||||
def convert_to_export(self, value, record):
|
||||
if not isinstance(self.selection, list):
|
||||
# FIXME: this reproduces an existing buggy behavior!
|
||||
return value if value else ''
|
||||
for item in self._description_selection(env):
|
||||
for item in self._description_selection(record.env):
|
||||
if item[0] == value:
|
||||
return item[1]
|
||||
return False
|
||||
@@ -1582,13 +1580,13 @@ class Reference(Selection):
|
||||
return False
|
||||
raise ValueError("Wrong value for %s: %r" % (self, value))
|
||||
|
||||
def convert_to_read(self, value, use_name_get=True):
|
||||
def convert_to_read(self, value, record, use_name_get=True):
|
||||
return "%s,%s" % (value._name, value.id) if value else False
|
||||
|
||||
def convert_to_export(self, value, env):
|
||||
def convert_to_export(self, value, record):
|
||||
return value.name_get()[0][1] if value else ''
|
||||
|
||||
def convert_to_display_name(self, value, record=None):
|
||||
def convert_to_display_name(self, value, record):
|
||||
return ustr(value and value.display_name)
|
||||
|
||||
|
||||
@@ -1627,8 +1625,8 @@ class _Relational(Field):
|
||||
_column_domain = property(attrgetter('domain'))
|
||||
_column_context = property(attrgetter('context'))
|
||||
|
||||
def null(self, env):
|
||||
return env[self.comodel_name]
|
||||
def null(self, record):
|
||||
return record.env[self.comodel_name]
|
||||
|
||||
def modified(self, records):
|
||||
# Invalidate cache for inverse fields, too. Note that the recomputation
|
||||
@@ -1699,9 +1697,9 @@ class Many2one(_Relational):
|
||||
elif isinstance(value, dict):
|
||||
return record.env[self.comodel_name].new(value)
|
||||
else:
|
||||
return self.null(record.env)
|
||||
return record.env[self.comodel_name]
|
||||
|
||||
def convert_to_read(self, value, use_name_get=True):
|
||||
def convert_to_read(self, value, record, use_name_get=True):
|
||||
if use_name_get and value:
|
||||
# evaluate name_get() as superuser, because the visibility of a
|
||||
# many2one field value (id and name) depends on the current record's
|
||||
@@ -1718,13 +1716,13 @@ class Many2one(_Relational):
|
||||
else:
|
||||
return value.id
|
||||
|
||||
def convert_to_write(self, value):
|
||||
def convert_to_write(self, value, record):
|
||||
return value.id
|
||||
|
||||
def convert_to_export(self, value, env):
|
||||
def convert_to_export(self, value, record):
|
||||
return value.name_get()[0][1] if value else ''
|
||||
|
||||
def convert_to_display_name(self, value, record=None):
|
||||
def convert_to_display_name(self, value, record):
|
||||
return ustr(value.display_name)
|
||||
|
||||
|
||||
@@ -1790,13 +1788,13 @@ class _RelationalMulti(_Relational):
|
||||
# return result as a recordset
|
||||
return comodel.browse(list(ids))
|
||||
elif not value:
|
||||
return self.null(record.env)
|
||||
return record.env[self.comodel_name]
|
||||
raise ValueError("Wrong value for %s: %s" % (self, value))
|
||||
|
||||
def convert_to_read(self, value, use_name_get=True):
|
||||
def convert_to_read(self, value, record, use_name_get=True):
|
||||
return value.ids
|
||||
|
||||
def convert_to_write(self, value):
|
||||
def convert_to_write(self, value, record):
|
||||
# make result with new and existing records
|
||||
result = [(5,)]
|
||||
for record in value:
|
||||
@@ -1805,21 +1803,22 @@ class _RelationalMulti(_Relational):
|
||||
values = record._convert_to_write(values)
|
||||
result.append((0, 0, values))
|
||||
elif record._is_dirty():
|
||||
values = {k: record._cache[k] for k in record._get_dirty()}
|
||||
values = {name: record[name] for name in record._get_dirty()}
|
||||
values = record._convert_to_write(values)
|
||||
result.append((1, record.id, values))
|
||||
else:
|
||||
result.append((4, record.id))
|
||||
return result
|
||||
|
||||
def convert_to_onchange(self, value, fnames=None):
|
||||
def convert_to_onchange(self, value, record, fnames=()):
|
||||
# return the recordset value as a list of commands; the commands may
|
||||
# give all fields values, the client is responsible for figuring out
|
||||
# which fields are actually dirty
|
||||
fields = [(name, value._fields[name]) for name in (fnames or []) if name != 'id']
|
||||
converters = [(name, value._fields[name].convert_to_onchange)
|
||||
for name in fnames if name != 'id']
|
||||
result = [(5,)]
|
||||
for record in value:
|
||||
vals = {name: field.convert_to_onchange(record[name]) for name, field in fields}
|
||||
vals = {name: convert(record[name], record) for name, convert in converters}
|
||||
if not record.id:
|
||||
result.append((0, 0, vals))
|
||||
elif vals:
|
||||
@@ -1828,10 +1827,10 @@ class _RelationalMulti(_Relational):
|
||||
result.append((4, record.id))
|
||||
return result
|
||||
|
||||
def convert_to_export(self, value, env):
|
||||
def convert_to_export(self, value, record):
|
||||
return ','.join(name for id, name in value.name_get()) if value else ''
|
||||
|
||||
def convert_to_display_name(self, value, record=None):
|
||||
def convert_to_display_name(self, value, record):
|
||||
raise NotImplementedError()
|
||||
|
||||
def _compute_related(self, records):
|
||||
@@ -1900,11 +1899,11 @@ class One2many(_RelationalMulti):
|
||||
_column_auto_join = property(attrgetter('auto_join'))
|
||||
_column_limit = property(attrgetter('limit'))
|
||||
|
||||
def convert_to_onchange(self, value, fnames=None):
|
||||
def convert_to_onchange(self, value, record, fnames=()):
|
||||
if fnames:
|
||||
# do not serialize self's inverse field
|
||||
fnames = [name for name in fnames if name != self.inverse_name]
|
||||
return super(One2many, self).convert_to_onchange(value, fnames)
|
||||
return super(One2many, self).convert_to_onchange(value, record, fnames)
|
||||
|
||||
|
||||
class Many2many(_RelationalMulti):
|
||||
|
||||
+12
-10
@@ -945,7 +945,7 @@ class BaseModel(object):
|
||||
# this part could be simpler, but it has to be done this way
|
||||
# in order to reproduce the former behavior
|
||||
if not isinstance(value, BaseModel):
|
||||
current[i] = field.convert_to_export(value, self.env)
|
||||
current[i] = field.convert_to_export(value, record)
|
||||
else:
|
||||
primary_done.append(name)
|
||||
|
||||
@@ -1383,7 +1383,6 @@ class BaseModel(object):
|
||||
parent_fields[field.model_name].append(field.name)
|
||||
|
||||
# convert default values to the right format
|
||||
defaults = self._convert_to_cache(defaults, validate=False)
|
||||
defaults = self._convert_to_write(defaults)
|
||||
|
||||
# add default values for inherited fields
|
||||
@@ -3393,7 +3392,7 @@ class BaseModel(object):
|
||||
try:
|
||||
values = {'id': record.id}
|
||||
for name, field in name_fields:
|
||||
values[name] = field.convert_to_read(record[name], use_name_get)
|
||||
values[name] = field.convert_to_read(record[name], record, use_name_get)
|
||||
result.append(values)
|
||||
except MissingError:
|
||||
pass
|
||||
@@ -5650,7 +5649,10 @@ class BaseModel(object):
|
||||
result = {}
|
||||
for name, value in values.iteritems():
|
||||
if name in fields:
|
||||
value = fields[name].convert_to_write(value)
|
||||
field = fields[name]
|
||||
value = field.convert_to_cache(value, self, validate=False)
|
||||
value = field.convert_to_record(value, self)
|
||||
value = field.convert_to_write(value, self)
|
||||
if not isinstance(value, NewId):
|
||||
result[name] = value
|
||||
return result
|
||||
@@ -5696,8 +5698,8 @@ class BaseModel(object):
|
||||
recs = self
|
||||
for name in name_seq.split('.'):
|
||||
field = recs._fields[name]
|
||||
null = field.null(self.env)
|
||||
recs = recs.mapped(lambda rec: rec._cache.get(field, null))
|
||||
null = field.convert_to_cache(False, self, validate=False)
|
||||
recs = recs.mapped(lambda rec: field.convert_to_record(rec._cache.get(field, null), rec))
|
||||
return recs
|
||||
|
||||
def filtered(self, func):
|
||||
@@ -6111,9 +6113,9 @@ class BaseModel(object):
|
||||
def __init__(self, record):
|
||||
self._record = record
|
||||
def __getitem__(self, name):
|
||||
field = self._record._fields[name]
|
||||
value = self._record[name]
|
||||
return field.convert_to_write(value)
|
||||
record = self._record
|
||||
field = record._fields[name]
|
||||
return field.convert_to_write(record[name], record)
|
||||
def __getattr__(self, name):
|
||||
return self[name]
|
||||
|
||||
@@ -6228,7 +6230,7 @@ class BaseModel(object):
|
||||
|
||||
# collect values from dirty fields
|
||||
result['value'] = {
|
||||
name: self._fields[name].convert_to_onchange(record[name], subfields.get(name))
|
||||
name: self._fields[name].convert_to_onchange(record[name], record, subfields[name])
|
||||
for name in dirty
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user