[FIX] models, fields, api: do not mix the cache format with the record format

This commit is contained in:
Raphael Collet
2016-04-25 13:37:11 +02:00
parent d035828778
commit 1ec7b28a09
3 changed files with 36 additions and 20 deletions
+6 -2
View File
@@ -1036,6 +1036,8 @@ class Environment(object):
def check_cache(self):
""" Check the cache consistency. """
from openerp.fields import SpecialValue
# make a full copy of the cache, and invalidate it
cache_dump = dict(
(field, dict(field_cache))
@@ -1051,9 +1053,11 @@ class Environment(object):
for record in records:
try:
cached = field_dump[record.id]
cached = cached.get() if isinstance(cached, SpecialValue) else cached
value = field.convert_to_record(cached, record)
fetched = record[field.name]
if fetched != cached:
info = {'cached': cached, 'fetched': fetched}
if fetched != value:
info = {'cached': value, 'fetched': fetched}
invalids.append((field, record, info))
except (AccessError, MissingError):
pass
+19 -16
View File
@@ -52,11 +52,12 @@ def copy_cache(records, env):
if record not in done:
done.add(record)
target = record.with_env(env)
for name, value in record._cache.iteritems():
for name in record._cache:
field = record._fields[name]
value = record[name]
if isinstance(value, BaseModel):
todo.update(value)
value = value.with_env(env)
target._cache[name] = value
target._cache[name] = field.convert_to_cache(value, target, validate=False)
def resolve_mro(model, name, predicate):
@@ -88,12 +89,9 @@ def default_new_to_old(field, value):
def default_old_to_new(field, value):
""" Convert the old-API default ``value`` to the new API. """
if callable(value):
return lambda model: field.convert_to_cache(
value(model._model, model._cr, model._uid, model._context),
model, validate=False,
)
return lambda model: value(model._model, model._cr, model._uid, model._context)
else:
return lambda model: field.convert_to_cache(value, model, validate=False)
return lambda model: value
def default_old_to_old(field, value):
""" Convert the old-API default ``value`` to the old API. """
@@ -860,7 +858,7 @@ class Field(object):
record._cache[self] = value
if env.in_onchange:
for invf in record._field_inverses[self]:
invf._update(value, record)
invf._update(record[self.name], record)
record._set_dirty(self.name)
# determine more dependent fields, and invalidate them
@@ -1685,8 +1683,11 @@ class Many2one(_Relational):
_column_auto_join = property(attrgetter('auto_join'))
def _update(self, records, value):
""" Update the cached value of ``self`` for ``records`` with ``value``. """
records._cache[self] = value
""" Update the cached value of ``self`` for ``records`` with ``value``.
This is used to reflect the assignment ``value[name] = records``, where
``name`` is the inverse field of ``self``.
"""
records._cache[self] = self.convert_to_cache(value, records, validate=False)
def convert_to_cache(self, value, record, validate=True):
if isinstance(value, (NoneType, int, long)):
@@ -1741,8 +1742,9 @@ class UnionUpdate(SpecialValue):
# in order to read the current field's value, remove self from cache
del record._cache[field]
# read the current field's value, and update it in cache only
record._cache[field] = new_value = record[field.name] | value
return new_value
value = field.convert_to_cache(record[field.name] | value, record, validate=False)
record._cache[field] = value
return value
class _RelationalMulti(_Relational):
@@ -1752,9 +1754,10 @@ class _RelationalMulti(_Relational):
""" Update the cached value of ``self`` for ``records`` with ``value``. """
for record in records:
if self in record._cache:
record._cache[self] = record[self.name] | value
value = self.convert_to_cache(record[self.name] | value, record, validate=False)
else:
record._cache[self] = UnionUpdate(self, record, value)
value = UnionUpdate(self, record, value)
record._cache[self] = value
def convert_to_cache(self, value, record, validate=True):
if isinstance(value, BaseModel):
@@ -1805,7 +1808,7 @@ class _RelationalMulti(_Relational):
result = [(5,)]
for record in value:
if not record.id:
values = dict(record._cache)
values = {name: record[name] for name in record._cache}
values = record._convert_to_write(values)
result.append((0, 0, values))
elif record._is_dirty():
+11 -2
View File
@@ -5665,6 +5665,15 @@ class BaseModel(object):
if name in fields
}
def _convert_to_record(self, values):
""" Convert the ``values`` dictionary from the cache format to the
record format.
"""
return {
name: self._fields[name].convert_to_record(value, self)
for name, value in values.iteritems()
}
def _convert_to_write(self, values):
""" Convert the ``values`` dictionary into the format of :meth:`write`. """
fields = self._fields
@@ -6099,7 +6108,7 @@ class BaseModel(object):
return
if res.get('value'):
res['value'].pop('id', None)
self.update(self._convert_to_cache(res['value'], validate=False))
self.update(res['value'])
if res.get('domain'):
result.setdefault('domain', {}).update(res['domain'])
if res.get('warning'):
@@ -6188,7 +6197,7 @@ class BaseModel(object):
# create a new record with values, and attach ``self`` to it
with env.do_in_onchange():
record = self.new(values)
values = dict(record._cache)
values = {name: record[name] for name in record._cache}
# attach ``self`` with a different context (for cache consistency)
record._origin = self.with_context(__onchange=True)