[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 <mbrown@fensystems.co.uk>
Closes #13688 (part 2/2)
This commit is contained in:
@@ -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):
|
||||
|
||||
@@ -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:
|
||||
|
||||
Reference in New Issue
Block a user