summaryrefslogtreecommitdiff
path: root/odoo/models.py
diff options
context:
space:
mode:
authorChristophe Simonis <chs@odoo.com>2019-02-20 13:40:48 +0100
committerChristophe Simonis <chs@odoo.com>2019-02-20 13:40:48 +0100
commitf1ff55bca1242af86cd5feedbf7c4793b96174a1 (patch)
tree164453a9cf678e004c2ae82819c51fbd57c0b6ae /odoo/models.py
parent716a272480e649db721c081605368e1bccf8dc91 (diff)
parentde8cefcef4d456398e5028c52e3bd9db807e5f8a (diff)
[MERGE] forward port branch 11.0 up to de8cefcef4d
Diffstat (limited to 'odoo/models.py')
-rw-r--r--odoo/models.py57
1 files changed, 37 insertions, 20 deletions
diff --git a/odoo/models.py b/odoo/models.py
index 538984a9be6..c9d89c175ed 100644
--- a/odoo/models.py
+++ b/odoo/models.py
@@ -2611,20 +2611,22 @@ class BaseModel(MetaModel('DummyModel', (object,), {'_register': False})):
# retrieve results from records; this takes values from the cache and
# computes remaining fields
self = self.with_prefetch(self._prefetch.copy())
- data = {record: {'id': record.id} for record in self}
- missing = set()
+ data = [(record, {'id': record._ids[0]}) for record in self]
use_name_get = (load == '_classic_read')
for name in (stored + inherited + computed):
convert = self._fields[name].convert_to_read
# restrict the prefetching of self's model to self; this avoids
# computing fields on a larger recordset than self
self._prefetch[self._name] = set(self._ids)
- for record in self:
+ for record, vals in data:
+ # missing records have their vals empty
+ if not vals:
+ continue
try:
- data[record][name] = convert(record[name], record, use_name_get)
+ vals[name] = convert(record[name], record, use_name_get)
except MissingError:
- missing.add(record)
- result = [data[record] for record in self if record not in missing]
+ vals.clear()
+ result = [vals for record, vals in data if vals]
return result
@@ -2719,36 +2721,46 @@ class BaseModel(MetaModel('DummyModel', (object,), {'_register': False})):
res = 'pg_size_pretty(length(%s)::bigint)' % res
return '%s as "%s"' % (res, col)
+ # selected fields are: 'id' followed by fields_pre
qual_names = [qualify(name) for name in [self._fields['id']] + fields_pre]
# determine the actual query to execute
from_clause, where_clause, params = query.get_sql()
query_str = "SELECT %s FROM %s WHERE %s" % (",".join(qual_names), from_clause, where_clause)
- result = []
+ # fetch one list of record values per field
+ field_values_list = [[] for name in qual_names]
param_pos = params.index(param_ids)
for sub_ids in cr.split_for_in_conditions(self.ids):
params[param_pos] = tuple(sub_ids)
cr.execute(query_str, params)
- result.extend(cr.dictfetchall())
+ for row in cr.fetchall():
+ for values, val in pycompat.izip(field_values_list, row):
+ values.append(val)
- ids = [vals['id'] for vals in result]
+ ids = field_values_list.pop(0)
fetched = self.browse(ids)
if ids:
# translate the fields if necessary
if context.get('lang'):
- for field in fields_pre:
+ for field, values in pycompat.izip(fields_pre, field_values_list):
if not field.inherited and callable(field.translate):
name = field.name
translate = field.get_trans_func(fetched)
- for vals in result:
- vals[name] = translate(vals['id'], vals[name])
+ for index in range(len(ids)):
+ values[index] = translate(ids[index], values[index])
# store result in cache
- for vals in result:
- record = self.browse(vals.pop('id'), self._prefetch)
- record._cache.update(record._convert_to_cache(vals, validate=False))
+ target = self.browse([], self._prefetch)
+ for field, values in pycompat.izip(fields_pre, field_values_list):
+ convert = field.convert_to_cache
+ # Note that the target record passed to convert below is empty.
+ # This does not harm in practice, as it is only used in Monetary
+ # fields for rounding the value. As the value comes straight
+ # from the database, it is expected to be rounded already.
+ values = [convert(value, target, validate=False) for value in values]
+ self.env.cache.update(fetched, field, values)
# determine the fields that must be processed now;
# for the sake of simplicity, we ignore inherited fields
@@ -4290,7 +4302,7 @@ class BaseModel(MetaModel('DummyModel', (object,), {'_register': False})):
#
@classmethod
- def _browse(cls, ids, env, prefetch=None):
+ def _browse(cls, ids, env, prefetch=None, add_prefetch=True):
""" Create a recordset instance.
:param ids: a tuple of record ids
@@ -4303,7 +4315,8 @@ class BaseModel(MetaModel('DummyModel', (object,), {'_register': False})):
if prefetch is None:
prefetch = defaultdict(set) # {model_name: set(ids)}
records._prefetch = prefetch
- prefetch[cls._name].update(ids)
+ if add_prefetch:
+ prefetch[cls._name].update(ids)
return records
def browse(self, arg=None, prefetch=None):
@@ -4342,9 +4355,13 @@ class BaseModel(MetaModel('DummyModel', (object,), {'_register': False})):
""" Verifies that the current recorset holds a single record. Raises
an exception otherwise.
"""
- if len(self) == 1:
+ try:
+ # unpack to ensure there is only one value is faster than len when true and
+ # has a significant impact as this check is largely called
+ _id, = self._ids
return self
- raise ValueError("Expected singleton: %s" % self)
+ except ValueError:
+ raise ValueError("Expected singleton: %s" % self)
def with_env(self, env):
""" Returns a new version of this recordset attached to the provided
@@ -4610,7 +4627,7 @@ class BaseModel(MetaModel('DummyModel', (object,), {'_register': False})):
def __iter__(self):
""" Return an iterator over ``self``. """
for id in self._ids:
- yield self._browse((id,), self.env, self._prefetch)
+ yield self._browse((id,), self.env, self._prefetch, False)
def __contains__(self, item):
""" Test whether ``item`` (record or field name) is an element of ``self``.