summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRaphael Collet <rco@openerp.com>2016-03-23 13:48:43 +0100
committerRaphael Collet <rco@openerp.com>2016-04-25 13:37:11 +0200
commit6037f3a56d3992cf0a6ff5ea1cb374dcbac03ea0 (patch)
tree0de70fc89d7aa75b98b5ff603c14b276ab44b8e2
parentcb8d432bb2211ee666b27ece6de86c9ef9f4f4ea (diff)
[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.
-rw-r--r--addons/event_sale/wizard/event_edit_registration.py1
-rw-r--r--addons/mail/wizard/mail_compose_message.py2
-rw-r--r--openerp/addons/base/ir/ir_values.py2
-rw-r--r--openerp/fields.py169
-rw-r--r--openerp/models.py22
5 files changed, 98 insertions, 98 deletions
diff --git a/addons/event_sale/wizard/event_edit_registration.py b/addons/event_sale/wizard/event_edit_registration.py
index 98f6553f5a6..989737405b8 100644
--- a/addons/event_sale/wizard/event_edit_registration.py
+++ b/addons/event_sale/wizard/event_edit_registration.py
@@ -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
diff --git a/addons/mail/wizard/mail_compose_message.py b/addons/mail/wizard/mail_compose_message.py
index 34d59736185..6537d6d1cf6 100644
--- a/addons/mail/wizard/mail_compose_message.py
+++ b/addons/mail/wizard/mail_compose_message.py
@@ -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}
diff --git a/openerp/addons/base/ir/ir_values.py b/openerp/addons/base/ir/ir_values.py
index 3af0f92249f..ce9f7328ede 100644
--- a/openerp/addons/base/ir/ir_values.py
+++ b/openerp/addons/base/ir/ir_values.py
@@ -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'):
diff --git a/openerp/fields.py b/openerp/fields.py
index 4d59bad64bf..63e2f2c3df4 100644
--- a/openerp/fields.py
+++ b/openerp/fields.py
@@ -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
+
+ 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, value's diplay name will
- be computed using :meth:`BaseModel.name_get`, if relevant
- for the field
+ :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, 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)
+ # cache miss, determine value and retrieve it
+ if record.id:
+ self.determine_value(record)
+ else:
+ self.determine_draft_value(record)
+ value = record._cache[self]
- # 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):
diff --git a/openerp/models.py b/openerp/models.py
index 0f49f5f222e..6d88734a913 100644
--- a/openerp/models.py
+++ b/openerp/models.py
@@ -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
}