From d477dcc31606cd41cb1622a5d078407fad2e57a7 Mon Sep 17 00:00:00 2001 From: Michael Brown Date: Mon, 8 Aug 2016 21:18:31 +0100 Subject: [PATCH] [FIX] models: fix behaviour of 'defer_parent_store_computation' If a model uses nested sets to maintain a hierarchical record ordering, then bulk inserts (e.g. via a CSV import) are likely to suffer from an O(n^2) performance penalty, since there is a high chance that each new record will require many of the existing records' parent_left and parent_right fields to be updated. The context parameter 'defer_parent_store_computation' may be passed to the load() method, in order to defer updating the parent_left and parent_right fields until after the bulk operation is complete. This appears to have last been fully supported in version 7 (via the old import_data() method). It is still possible to specify 'defer_parent_store_computation', e.g. via an XML-RPC call such as m.execute_kw(dbname, uid, password, 'stock.location', 'load', [], {'fields': fields, 'data': data, 'context': {'defer_parent_store_computation': True}}) This will currently defer the recalculation of parent_left and parent_right, but does not correctly call _parent_store_compute() after the bulk operation is complete. Add the missing call to _parent_store_compute() to perform a one-time update of the parent_left and parent_right fields if needed after the import operation is complete. Also fix the child_of_domain() and parent_of_domain() expression methods, so that the 'child_of' and 'parent_of' operators give correct answers when updates to parent_left and parent_right have been deferred. Signed-off-by: Michael Brown Closes #13688 (part 2/2) --- openerp/models.py | 6 ++++++ openerp/osv/expression.py | 8 ++++++-- 2 files changed, 12 insertions(+), 2 deletions(-) diff --git a/openerp/models.py b/openerp/models.py index a9e229c3ba6..094d704f603 100644 --- a/openerp/models.py +++ b/openerp/models.py @@ -1040,6 +1040,8 @@ class BaseModel(object): :param dict context: :returns: {ids: list(int)|False, messages: [Message]} """ + if context is None: + context = {} cr.execute('SAVEPOINT model_load') messages = [] @@ -1094,6 +1096,10 @@ class BaseModel(object): if any(message['type'] == 'error' for message in messages): cr.execute('ROLLBACK TO SAVEPOINT model_load') ids = False + + if ids and context.get('defer_parent_store_computation'): + self._parent_store_compute(cr) + return {'ids': ids, 'messages': messages} def _add_fake_fields(self, cr, uid, fields, context=None): diff --git a/openerp/osv/expression.py b/openerp/osv/expression.py index 8b55fe3f646..71d5c4303c8 100644 --- a/openerp/osv/expression.py +++ b/openerp/osv/expression.py @@ -711,9 +711,11 @@ class expression(object): """ Return a domain implementing the child_of operator for [(left,child_of,ids)], either as a range using the parent_left/right tree lookup fields (when available), or as an expanded [(left,in,child_ids)] """ + if context is None: + context = {} if not ids: return FALSE_DOMAIN - if left_model._parent_store and (not left_model.pool._init): + if left_model._parent_store and (not left_model.pool._init) and (not context.get('defer_parent_store_computation')): # TODO: Improve where joins are implemented for many with '.', replace by: # doms += ['&',(prefix+'.parent_left','<',o.parent_right),(prefix+'.parent_left','>=',o.parent_left)] doms = [] @@ -736,7 +738,9 @@ class expression(object): """ Return a domain implementing the parent_of operator for [(left,parent_of,ids)], either as a range using the parent_left/right tree lookup fields (when available), or as an expanded [(left,in,parent_ids)] """ - if left_model._parent_store and (not left_model.pool._init): + if context is None: + context = {} + if left_model._parent_store and (not left_model.pool._init) and (not context.get('defer_parent_store_computation')): doms = [] for node in left_model.browse(cr, uid, ids, context=context): if doms: