diff --git a/trytond/trytond/model/fields/many2many.py b/trytond/trytond/model/fields/many2many.py index 901125acf9c..77a833e3ff8 100644 --- a/trytond/trytond/model/fields/many2many.py +++ b/trytond/trytond/model/fields/many2many.py @@ -140,6 +140,7 @@ def get(self, ids, model, name, values=None): Relation = self.get_relation() origin_field = Relation._fields[self.origin] + reference_key = origin_field._type == 'reference' if (not isinstance(origin_field, Function) or hasattr(Relation, 'order_' + self.field)): @@ -153,7 +154,7 @@ def get(self, ids, model, name, values=None): relations = [] for sub_ids in grouped_slice(ids): - if origin_field._type == 'reference': + if reference_key: references = ['%s,%s' % (model.__name__, x) for x in sub_ids] clause = [(self.origin, 'in', references)] else: @@ -162,11 +163,16 @@ def get(self, ids, model, name, values=None): if self.filter: clause.append((self.target, 'where', self.filter)) relations.append(Relation.search(clause, order=order)) - relations = Relation.browse(list(chain(*relations))) + relations = Relation.read( + list(chain(*relations)), [self.origin, self.target]) for relation in relations: - origin_id = getattr(relation, self.origin).id - res[origin_id].append(getattr(relation, self.target).id) + if reference_key: + _, origin_id = relation[self.origin].split(',', 1) + origin_id = int(origin_id) + else: + origin_id = relation[self.origin] + res[origin_id].append(relation[self.target]) return dict((key, tuple(value)) for key, value in res.items()) def set(self, Model, name, ids, values, *args): diff --git a/trytond/trytond/model/fields/one2many.py b/trytond/trytond/model/fields/one2many.py index 0cd7b659264..9d03ad7cd8e 100644 --- a/trytond/trytond/model/fields/one2many.py +++ b/trytond/trytond/model/fields/one2many.py @@ -143,6 +143,7 @@ def get(self, ids, model, name, values=None): ''' Target = self.get_target() field = Target._fields[self.field] + reference_o2m = field._type == 'reference' res = {} for i in ids: res[i] = [] @@ -158,7 +159,7 @@ def get(self, ids, model, name, values=None): order += Target._order targets = [] for sub_ids in grouped_slice(ids): - if field._type == 'reference': + if reference_o2m: references = ['%s,%s' % (model.__name__, x) for x in sub_ids] clause = [(self.field, 'in', references)] else: @@ -166,11 +167,15 @@ def get(self, ids, model, name, values=None): if self.filter: clause.append(self.filter) targets.append(Target.search(clause, order=order)) - targets = Target.browse(list(chain(*targets))) + targets = Target.read(list(chain(*targets)), ['id', self.field]) for target in targets: - origin_id = getattr(target, self.field).id - res[origin_id].append(target.id) + if reference_o2m: + _, origin_id = target[self.field].split(',', 1) + origin_id = int(origin_id) + else: + origin_id = target[self.field] + res[origin_id].append(target['id']) return dict((key, tuple(value)) for key, value in res.items()) def set(self, Model, name, ids, values, *args): diff --git a/trytond/trytond/model/modelstorage.py b/trytond/trytond/model/modelstorage.py index 1d3fe08b703..aa43ce809ea 100644 --- a/trytond/trytond/model/modelstorage.py +++ b/trytond/trytond/model/modelstorage.py @@ -1663,6 +1663,8 @@ def __getattr__(self, name): except KeyError: skip_eager = False + pool = Pool() + # build the list of fields we will fetch ffields = { name: field, @@ -1676,7 +1678,7 @@ def __getattr__(self, name): multiple_getter = field.getter if load_eager or multiple_getter: - FieldAccess = Pool().get('ir.model.field.access') + FieldAccess = pool.get('ir.model.field.access') fread_accesses = {} fread_accesses.update(FieldAccess.check(self.__name__, list(self._fields.keys()), 'read', access=True)) @@ -1752,6 +1754,30 @@ def unique(ids): islice(self._ids, 0, max(index - 1, 0))) ids = islice(unique(filter(filter_, ids)), read_size) + kwargs_cache = {} + transaction = Transaction() + + def create_instances(Model, value, cache_key=None): + cache_key = (Model, cache_key) + if cache_key in kwargs_cache: + kwargs = kwargs_cache[cache_key] + else: + if cache_key not in model2cache: + model2cache[cache_key] = local_cache(Model, transaction) + kwargs_cache[cache_key] = kwargs = { + '_local_cache': model2cache[cache_key], + '_ids': model2ids.setdefault(cache_key, []), + '_transaction_cache': transaction.get_cache(), + '_transaction': transaction, + } + ids = kwargs['_ids'] + if field._type in ('many2one', 'one2one', 'reference'): + ids.append(value) + return Model(value, **kwargs) + elif field._type in ('one2many', 'many2many'): + ids.extend(value) + return tuple(Model(id, **kwargs) for id in value) + def instantiate(field, value, data): if field._type in ('many2one', 'one2one', 'reference'): # ABDC: Fix when data is an empty string, we should return @@ -1764,7 +1790,7 @@ def instantiate(field, value, data): try: if field._type == 'reference': model_name, record_id = value.split(',') - Model = Pool().get(model_name) + Model = pool.get(model_name) try: record_id = int(record_id) except ValueError: @@ -1777,30 +1803,18 @@ def instantiate(field, value, data): except KeyError: return value transaction = Transaction() - ctx = {} - if field.context: - pyson_context = PYSONEncoder().encode(field.context) - ctx.update(PYSONDecoder(data).decode(pyson_context)) - datetime_ = None - if getattr(field, 'datetime_field', None): - datetime_ = data.get(field.datetime_field) - ctx = {'_datetime': datetime_} - with transaction.set_context(**ctx): - kwargs = {} - key = (Model, freeze(ctx)) - if key not in model2cache: - model2cache[key] = local_cache(Model, transaction) - kwargs['_local_cache'] = model2cache[key] - kwargs['_ids'] = ids = model2ids.setdefault(key, []) - kwargs['_transaction_cache'] = transaction.get_cache() - kwargs['_transaction'] = transaction - if field._type in ('many2one', 'one2one', 'reference'): - value = int(value) - ids.append(value) - return Model(value, **kwargs) - elif field._type in ('one2many', 'many2many'): - ids.extend(int(x) for x in value) - return tuple(Model(id, **kwargs) for id in value) + if ((dt_field := getattr(field, 'datetime_field', None)) + or field.context): + ctx = {} + if field.context: + pyson_context = PYSONEncoder().encode(field.context) + ctx.update(PYSONDecoder(data).decode(pyson_context)) + if dt_field: + ctx['_datetime'] = data.get(dt_field) + with transaction.set_context(**ctx): + return create_instances(Model, value, freeze(ctx)) + else: + return create_instances(Model, value) model2ids = {} model2cache = {}