[IMP] fields: speedup of `convert_to_cache` for x2many fields

The algorithm's complexity changed from O(n²) to O(n).
This commit is contained in:
Raphael Collet 2015-06-30 16:23:16 +02:00
parent 9e8d29c986
commit 0939738479
2 changed files with 28 additions and 18 deletions

View File

@ -30,7 +30,7 @@ import logging
import pytz import pytz
import xmlrpclib 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_DATE_FORMAT as DATE_FORMAT
from openerp.tools import DEFAULT_SERVER_DATETIME_FORMAT as DATETIME_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) return value.with_env(record.env)
elif isinstance(value, list): elif isinstance(value, list):
# value is a list of record ids or commands # value is a list of record ids or commands
if not record.id: comodel = record.env[self.comodel_name]
record = record.browse() # new record has no value ids = OrderedSet(record[self.name].ids)
result = record[self.name] # modify ids with the commands
# modify result with the commands;
# beware to not introduce duplicates in result
for command in value: for command in value:
if isinstance(command, (tuple, list)): if isinstance(command, (tuple, list)):
if command[0] == 0: if command[0] == 0:
result += result.new(command[2]) ids.add(comodel.new(command[2]).id)
elif command[0] == 1: elif command[0] == 1:
result.browse(command[1]).update(command[2]) comodel.browse(command[1]).update(command[2])
result += result.browse(command[1]) - result ids.add(command[1])
elif command[0] == 2: elif command[0] == 2:
# note: the record will be deleted by write() # note: the record will be deleted by write()
result -= result.browse(command[1]) ids.discard(command[1])
elif command[0] == 3: elif command[0] == 3:
result -= result.browse(command[1]) ids.discard(command[1])
elif command[0] == 4: elif command[0] == 4:
result += result.browse(command[1]) - result ids.add(command[1])
elif command[0] == 5: elif command[0] == 5:
result = result.browse() ids.clear()
elif command[0] == 6: elif command[0] == 6:
result = result.browse(command[2]) ids = OrderedSet(command[2])
elif isinstance(command, dict): elif isinstance(command, dict):
result += result.new(command) ids.add(comodel.new(command).id)
else: else:
result += result.browse(command) - result ids.add(command)
return result # return result as a recordset
return comodel.browse(list(ids))
elif not value: elif not value:
return self.null(record.env) return self.null(record.env)
raise ValueError("Wrong value for %s: %s" % (self, value)) raise ValueError("Wrong value for %s: %s" % (self, value))

View File

@ -37,7 +37,7 @@ import threading
import time import time
import werkzeug.utils import werkzeug.utils
import zipfile import zipfile
from collections import defaultdict, Mapping from collections import defaultdict, Mapping, OrderedDict
from datetime import datetime from datetime import datetime
from itertools import islice, izip, groupby from itertools import islice, izip, groupby
from lxml import etree from lxml import etree
@ -1271,6 +1271,17 @@ class frozendict(dict):
def update(self, *args, **kwargs): def update(self, *args, **kwargs):
raise NotImplementedError("'update' not supported on frozendict") 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 @contextmanager
def ignore(*exc): def ignore(*exc):
try: try: