From 7a8a8097f689360bda543f4e6cb00a410f163c09 Mon Sep 17 00:00:00 2001 From: Raphael Collet Date: Mon, 11 Apr 2016 14:24:53 +0200 Subject: [PATCH] [IMP] fields: change the cache format of relational fields Relational fields are now cached as tuples of ids instead of recordsets. --- openerp/addons/base/tests/test_api.py | 2 +- openerp/fields.py | 48 ++++++++++++++++++--------- openerp/models.py | 4 +-- 3 files changed, 35 insertions(+), 19 deletions(-) diff --git a/openerp/addons/base/tests/test_api.py b/openerp/addons/base/tests/test_api.py index 6f623b3f3f4..645c5d4ad9b 100644 --- a/openerp/addons/base/tests/test_api.py +++ b/openerp/addons/base/tests/test_api.py @@ -339,7 +339,7 @@ class TestAPI(common.TransactionCase): self.assertItemsEqual(partners.ids, country_id_cache) # partners' countries are ready for prefetching - country_ids = set(c.id for c in country_id_cache.itervalues() if c) + country_ids = set(cid for cids in country_id_cache.itervalues() for cid in cids) self.assertTrue(len(country_ids) > 1) self.assertItemsEqual(country_ids, partners._prefetch['res.country']) diff --git a/openerp/fields.py b/openerp/fields.py index 9bb67944254..bafd4081314 100644 --- a/openerp/fields.py +++ b/openerp/fields.py @@ -1571,16 +1571,24 @@ class Reference(Selection): "Reference field %s with non-integer size %r" % (self, self.size) def convert_to_cache(self, value, record, validate=True): + # cache format: (res_model, res_id) or False + def process(res_model, res_id): + record._prefetch[res_model].add(res_id) + return (res_model, res_id) + if isinstance(value, BaseModel): if not validate or (value._name in self.get_values(record.env) and len(value) <= 1): - return value.with_env(record.env).with_prefetch(record._prefetch) or False + return process(value._name, value.id) if value else False elif isinstance(value, basestring): res_model, res_id = value.split(',') - return record.env[res_model].browse(int(res_id), record._prefetch) + return process(res_model, int(res_id)) elif not value: return False raise ValueError("Wrong value for %s: %r" % (self, value)) + def convert_to_record(self, value, record): + return value and record.env[value[0]].browse([value[1]], record._prefetch) + def convert_to_read(self, value, record, use_name_get=True): return "%s,%s" % (value._name, value.id) if value else False @@ -1690,21 +1698,25 @@ class Many2one(_Relational): 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)): - return record.env[self.comodel_name].browse(value, record._prefetch) - if isinstance(value, BaseModel): + # cache format: tuple(ids) + def process(ids): + return record._prefetch[self.comodel_name].update(ids) or ids + + if type(value) in IdType: + return process((value,)) + elif isinstance(value, BaseModel): if not validate or (value._name == self.comodel_name and len(value) <= 1): - return value.with_env(record.env).with_prefetch(record._prefetch) + return process(value._ids) raise ValueError("Wrong value for %s: %r" % (self, value)) elif isinstance(value, tuple): - return record.env[self.comodel_name].browse(value[0], record._prefetch) + return process((value[0],)) elif isinstance(value, dict): - return record.env[self.comodel_name].new(value) + return process(record.env[self.comodel_name].new(value)._ids) else: - return record.env[self.comodel_name] + return () def convert_to_record(self, value, record): - return value.with_prefetch(record._prefetch) + return record.env[self.comodel_name]._browse(value, record.env, record._prefetch) def convert_to_read(self, value, record, use_name_get=True): if use_name_get and value: @@ -1760,9 +1772,13 @@ class _RelationalMulti(_Relational): record._cache[self] = value def convert_to_cache(self, value, record, validate=True): + # cache format: tuple(ids) + def process(ids): + return record._prefetch[self.comodel_name].update(ids) or ids + if isinstance(value, BaseModel): if not validate or (value._name == self.comodel_name): - return value.with_env(record.env).with_prefetch(record._prefetch) + return process(value._ids) elif isinstance(value, list): # value is a list of record ids or commands comodel = record.env[self.comodel_name] @@ -1791,14 +1807,14 @@ class _RelationalMulti(_Relational): ids.add(comodel.new(command).id) else: ids.add(command) - # return result as a recordset - return comodel.browse(list(ids), record._prefetch) + # return result as a tuple + return process(tuple(ids)) elif not value: - return record.env[self.comodel_name] + return () raise ValueError("Wrong value for %s: %s" % (self, value)) def convert_to_record(self, value, record): - return value.with_prefetch(record._prefetch) + return record.env[self.comodel_name]._browse(value, record.env, record._prefetch) def convert_to_read(self, value, record, use_name_get=True): return value.ids @@ -2030,5 +2046,5 @@ class Id(Field): # imported here to avoid dependency cycle issues from openerp import SUPERUSER_ID from .exceptions import AccessError, MissingError -from .models import check_pg_name, BaseModel +from .models import check_pg_name, BaseModel, IdType from .osv import fields diff --git a/openerp/models.py b/openerp/models.py index 3b3c093c0a7..35356269004 100644 --- a/openerp/models.py +++ b/openerp/models.py @@ -269,7 +269,7 @@ class NewId(object): def __nonzero__(self): return False -IdType = (int, long, basestring, NewId) +IdType = (int, long, str, unicode, NewId) # maximum number of prefetched records @@ -6422,7 +6422,7 @@ PGERROR_TO_OE = defaultdict( '23505': convert_pgerror_23505, }) -def _normalize_ids(arg, atoms={int, long, str, unicode, NewId}): +def _normalize_ids(arg, atoms=set(IdType)): """ Normalizes the ids argument for ``browse`` (v7 and v8) to a tuple. Various implementations were tested on the corpus of all browse() calls