[IMP] models: in _read_from_database(), fetch inherited fields when possible
This should improve the performance of method read() on models with inherited fields, like product.product. The inherited fields that are stored as columns in parent tables (except for translated fields) are read in the same query as the fields of the model. Those fields will be directly stored in cache under the main model, so that no copying will take place in cache for accessing them (this is the default implementation of inherited fields).
This commit is contained in:
parent
2c261a2987
commit
04ba0e99a4
|
@ -3146,17 +3146,20 @@ class BaseModel(object):
|
||||||
fields = self.check_field_access_rights('read', fields)
|
fields = self.check_field_access_rights('read', fields)
|
||||||
|
|
||||||
# split fields into stored and computed fields
|
# split fields into stored and computed fields
|
||||||
stored, computed = [], []
|
stored, inherited, computed = [], [], []
|
||||||
for name in fields:
|
for name in fields:
|
||||||
if name in self._columns:
|
if name in self._columns:
|
||||||
stored.append(name)
|
stored.append(name)
|
||||||
elif name in self._fields:
|
elif name in self._fields:
|
||||||
computed.append(name)
|
computed.append(name)
|
||||||
|
field = self._fields[name]
|
||||||
|
if field.inherited and field.base_field.column:
|
||||||
|
inherited.append(name)
|
||||||
else:
|
else:
|
||||||
_logger.warning("%s.read() with unknown field '%s'", self._name, name)
|
_logger.warning("%s.read() with unknown field '%s'", self._name, name)
|
||||||
|
|
||||||
# fetch stored fields from the database to the cache
|
# fetch stored fields from the database to the cache
|
||||||
self._read_from_database(stored)
|
self._read_from_database(stored, inherited)
|
||||||
|
|
||||||
# retrieve results from records; this takes values from the cache and
|
# retrieve results from records; this takes values from the cache and
|
||||||
# computes remaining fields
|
# computes remaining fields
|
||||||
|
@ -3225,9 +3228,14 @@ class BaseModel(object):
|
||||||
self._cache[field] = FailedValue(e)
|
self._cache[field] = FailedValue(e)
|
||||||
|
|
||||||
@api.multi
|
@api.multi
|
||||||
def _read_from_database(self, field_names):
|
def _read_from_database(self, field_names, inherited_field_names=[]):
|
||||||
""" Read the given fields of the records in `self` from the database,
|
""" Read the given fields of the records in `self` from the database,
|
||||||
and store them in cache. Access errors are also stored in cache.
|
and store them in cache. Access errors are also stored in cache.
|
||||||
|
|
||||||
|
:param field_names: list of column names of model `self`; all those
|
||||||
|
fields are guaranteed to be read
|
||||||
|
:param inherited_field_names: list of column names from parent
|
||||||
|
models; some of those fields may not be read
|
||||||
"""
|
"""
|
||||||
env = self.env
|
env = self.env
|
||||||
cr, user, context = env.args
|
cr, user, context = env.args
|
||||||
|
@ -3237,18 +3245,29 @@ class BaseModel(object):
|
||||||
self._apply_ir_rules(query, 'read')
|
self._apply_ir_rules(query, 'read')
|
||||||
order_str = self._generate_order_by(None, query)
|
order_str = self._generate_order_by(None, query)
|
||||||
|
|
||||||
# determine the fields that are stored as columns in self._table
|
# determine the fields that are stored as columns in tables;
|
||||||
fields_pre = [f for f in field_names if self._columns[f]._classic_write]
|
# for the sake of simplicity, discard inherited translated fields
|
||||||
|
fields = map(self._fields.get, field_names + inherited_field_names)
|
||||||
|
fields_pre = [
|
||||||
|
field
|
||||||
|
for field in fields
|
||||||
|
if field.base_field.column._classic_write
|
||||||
|
if not (field.inherited and field.base_field.column.translate)
|
||||||
|
]
|
||||||
|
|
||||||
# the query may involve several tables: we need fully-qualified names
|
# the query may involve several tables: we need fully-qualified names
|
||||||
def qualify(f):
|
def qualify(field):
|
||||||
if isinstance(self._columns.get(f), fields.binary) and \
|
col = field.name
|
||||||
context.get('bin_size_%s' % f, context.get('bin_size')):
|
if field.inherited:
|
||||||
# PG 9.2 introduces conflicting pg_size_pretty(numeric) -> need ::cast
|
res = self._inherits_join_calc(field.name, query)
|
||||||
return 'pg_size_pretty(length(%s."%s")::bigint) as "%s"' % (self._table, f, f)
|
|
||||||
else:
|
else:
|
||||||
return '%s."%s"' % (self._table, f)
|
res = '"%s"."%s"' % (self._table, col)
|
||||||
qual_names = map(qualify, set(fields_pre + ['id']))
|
if field.type == 'binary' and (context.get('bin_size') or context.get('bin_size_' + col)):
|
||||||
|
# PG 9.2 introduces conflicting pg_size_pretty(numeric) -> need ::cast
|
||||||
|
res = 'pg_size_pretty(length(%s)::bigint) as "%s"' % (res, col)
|
||||||
|
return res
|
||||||
|
|
||||||
|
qual_names = map(qualify, set(fields_pre + [self._fields['id']]))
|
||||||
|
|
||||||
# determine the actual query to execute
|
# determine the actual query to execute
|
||||||
from_clause, where_clause, where_params = query.get_sql()
|
from_clause, where_clause, where_params = query.get_sql()
|
||||||
|
@ -3272,8 +3291,9 @@ class BaseModel(object):
|
||||||
# translate the fields if necessary
|
# translate the fields if necessary
|
||||||
if context.get('lang'):
|
if context.get('lang'):
|
||||||
ir_translation = env['ir.translation']
|
ir_translation = env['ir.translation']
|
||||||
for f in fields_pre:
|
for field in fields_pre:
|
||||||
if self._columns[f].translate:
|
if not field.inherited and field.column.translate:
|
||||||
|
f = field.name
|
||||||
#TODO: optimize out of this loop
|
#TODO: optimize out of this loop
|
||||||
res_trans = ir_translation._get_ids(
|
res_trans = ir_translation._get_ids(
|
||||||
'%s,%s' % (self._name, f), 'model', context['lang'], ids)
|
'%s,%s' % (self._name, f), 'model', context['lang'], ids)
|
||||||
|
@ -3281,9 +3301,10 @@ class BaseModel(object):
|
||||||
vals[f] = res_trans.get(vals['id'], False) or vals[f]
|
vals[f] = res_trans.get(vals['id'], False) or vals[f]
|
||||||
|
|
||||||
# apply the symbol_get functions of the fields we just read
|
# apply the symbol_get functions of the fields we just read
|
||||||
for f in fields_pre:
|
for field in fields_pre:
|
||||||
symbol_get = self._columns[f]._symbol_get
|
symbol_get = field.base_field.column._symbol_get
|
||||||
if symbol_get:
|
if symbol_get:
|
||||||
|
f = field.name
|
||||||
for vals in result:
|
for vals in result:
|
||||||
vals[f] = symbol_get(vals[f])
|
vals[f] = symbol_get(vals[f])
|
||||||
|
|
||||||
|
@ -3292,7 +3313,8 @@ class BaseModel(object):
|
||||||
record = self.browse(vals['id'])
|
record = self.browse(vals['id'])
|
||||||
record._cache.update(record._convert_to_cache(vals, validate=False))
|
record._cache.update(record._convert_to_cache(vals, validate=False))
|
||||||
|
|
||||||
# determine the fields that must be processed now
|
# determine the fields that must be processed now;
|
||||||
|
# for the sake of simplicity, we ignore inherited fields
|
||||||
fields_post = [f for f in field_names if not self._columns[f]._classic_write]
|
fields_post = [f for f in field_names if not self._columns[f]._classic_write]
|
||||||
|
|
||||||
# Compute POST fields, grouped by multi
|
# Compute POST fields, grouped by multi
|
||||||
|
|
Loading…
Reference in New Issue