From 09397384795f440b5924d9b325988e9cc6eb4d09 Mon Sep 17 00:00:00 2001 From: Raphael Collet Date: Tue, 30 Jun 2015 16:23:16 +0200 Subject: [PATCH] [IMP] fields: speedup of `convert_to_cache` for x2many fields MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The algorithm's complexity changed from O(n²) to O(n). --- openerp/fields.py | 33 ++++++++++++++++----------------- openerp/tools/misc.py | 13 ++++++++++++- 2 files changed, 28 insertions(+), 18 deletions(-) diff --git a/openerp/fields.py b/openerp/fields.py index 9d3fb496044..e9dbfaa9104 100644 --- a/openerp/fields.py +++ b/openerp/fields.py @@ -30,7 +30,7 @@ import logging import pytz import xmlrpclib -from openerp.tools import float_round, frozendict, html_sanitize, ustr +from openerp.tools import float_round, frozendict, html_sanitize, ustr, OrderedSet from openerp.tools import DEFAULT_SERVER_DATE_FORMAT as DATE_FORMAT from openerp.tools import DEFAULT_SERVER_DATETIME_FORMAT as DATETIME_FORMAT @@ -1606,34 +1606,33 @@ class _RelationalMulti(_Relational): return value.with_env(record.env) elif isinstance(value, list): # value is a list of record ids or commands - if not record.id: - record = record.browse() # new record has no value - result = record[self.name] - # modify result with the commands; - # beware to not introduce duplicates in result + comodel = record.env[self.comodel_name] + ids = OrderedSet(record[self.name].ids) + # modify ids with the commands for command in value: if isinstance(command, (tuple, list)): if command[0] == 0: - result += result.new(command[2]) + ids.add(comodel.new(command[2]).id) elif command[0] == 1: - result.browse(command[1]).update(command[2]) - result += result.browse(command[1]) - result + comodel.browse(command[1]).update(command[2]) + ids.add(command[1]) elif command[0] == 2: # note: the record will be deleted by write() - result -= result.browse(command[1]) + ids.discard(command[1]) elif command[0] == 3: - result -= result.browse(command[1]) + ids.discard(command[1]) elif command[0] == 4: - result += result.browse(command[1]) - result + ids.add(command[1]) elif command[0] == 5: - result = result.browse() + ids.clear() elif command[0] == 6: - result = result.browse(command[2]) + ids = OrderedSet(command[2]) elif isinstance(command, dict): - result += result.new(command) + ids.add(comodel.new(command).id) else: - result += result.browse(command) - result - return result + ids.add(command) + # return result as a recordset + return comodel.browse(list(ids)) elif not value: return self.null(record.env) raise ValueError("Wrong value for %s: %s" % (self, value)) diff --git a/openerp/tools/misc.py b/openerp/tools/misc.py index 36b12893bc4..35f570fb7b6 100644 --- a/openerp/tools/misc.py +++ b/openerp/tools/misc.py @@ -37,7 +37,7 @@ import threading import time import werkzeug.utils import zipfile -from collections import defaultdict, Mapping +from collections import defaultdict, Mapping, OrderedDict from datetime import datetime from itertools import islice, izip, groupby from lxml import etree @@ -1271,6 +1271,17 @@ class frozendict(dict): def update(self, *args, **kwargs): raise NotImplementedError("'update' not supported on frozendict") +class OrderedSet(OrderedDict): + """ A simple collection that remembers the elements insertion order. """ + def __init__(self, seq=()): + super(OrderedSet, self).__init__([(x, None) for x in seq]) + + def add(self, elem): + self[elem] = None + + def discard(self, elem): + self.pop(elem, None) + @contextmanager def ignore(*exc): try: