From 8f9e1e40a32748e576084320b4f9a4c1165b7a10 Mon Sep 17 00:00:00 2001 From: Aaron Bohy Date: Wed, 10 Jul 2019 12:49:19 +0000 Subject: [FIX] web: batch read of m2m in o2m after onchange MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Let's assume a one2many field (editable list) in a form view with a many2many (e.g. many2many_tags), and an onchange on the one2many such that, when a row is added to the relation, the server returns update commands for (a subset of) the records being already in the relation. For thoses updated records, the many2many field needs to be read (the onchange only returns the ids in the relation). Before this rev., the many2many field was read independently for each record in the one2many. This could cause a performance issue on large relations. For instance, this was the case on account.invoice records with a lot of lines. This commit must be forwardported up to 12.2, not further. Issue 2027356 closes odoo/odoo#34771 Signed-off-by: Géry Debongnie (ged) --- .../web/static/src/js/views/basic/basic_model.js | 104 ++++++++++++++++++++- 1 file changed, 100 insertions(+), 4 deletions(-) (limited to 'addons/web/static/src/js/views/basic/basic_model.js') diff --git a/addons/web/static/src/js/views/basic/basic_model.js b/addons/web/static/src/js/views/basic/basic_model.js index cb48e09fe98..43e7a82ddf9 100644 --- a/addons/web/static/src/js/views/basic/basic_model.js +++ b/addons/web/static/src/js/views/basic/basic_model.js @@ -144,6 +144,8 @@ var BasicModel = AbstractModel.extend({ 'ir.filters', 'ir.ui.view', ], + // FORWARDPORT THIS UP TO 12.2, NOT FURTHER + disableBatchedRPCs: false, // to be overriden in tests /** * @override @@ -154,6 +156,11 @@ var BasicModel = AbstractModel.extend({ // save is performed. this.mutex = new concurrency.Mutex(); + // FORWARDPORT THIS UP TO 12.2, NOT FURTHER + // this array is used to accumulate RPC requests done in the same call + // stack, so that they can be batched in the minimum number of RPCs + this.batchedRPCsRequests = []; + this.localData = Object.create(null); this._super.apply(this, arguments); }, @@ -3947,6 +3954,93 @@ var BasicModel = AbstractModel.extend({ return $.when.apply($, defs); }, + // FORWARDPORT THIS UP TO 12.2, NOT FURTHER + /** + * Empty the pool of accumulated RPC requests: regroup similar requests in + * batches and perform an RPC for each batch. + * + * @private + */ + _performBatchedRPCs: function () { + if (!this.batchedRPCsRequests.length) { + // pool has already been processed + return; + } + + // reset pool of RPC requests + var batchedRPCsRequests = this.batchedRPCsRequests; + this.batchedRPCsRequests = []; + + // batch similar requests + var batches = {}; + var key; + for (var i = 0; i < batchedRPCsRequests.length; i++) { + var request = batchedRPCsRequests[i]; + key = request.model + ',' + JSON.stringify(request.context); + if (!batches[key]) { + batches[key] = _.extend({}, request, {requests: [request]}); + } else { + batches[key].ids = _.uniq(batches[key].ids.concat(request.ids)); + batches[key].fieldNames = _.uniq(batches[key].fieldNames.concat(request.fieldNames)); + batches[key].requests.push(request); + } + } + + // perform batched RPCs + function onSuccess(batch, results) { + for (var i = 0; i < batch.requests.length; i++) { + var request = batch.requests[i]; + var fieldNames = request.fieldNames.concat(['id']); + var filteredResults = results.filter(function (record) { + return request.ids.indexOf(record.id) >= 0; + }).map(function (record) { + return _.pick(record, fieldNames); + }); + request.def.resolve(filteredResults); + } + } + function onFailure(batch, error) { + for (var i = 0; i < batch.requests.length; i++) { + var request = batch.requests[i]; + request.def.reject(error); + } + } + for (key in batches) { + var batch = batches[key]; + this._rpc({ + model: batch.model, + method: 'read', + args: [batch.ids, batch.fieldNames], + context: batch.context, + }).then(onSuccess.bind(null, batch)).fail(onFailure.bind(null, batch)); + } + }, + /** + * This function accumulates RPC requests done in the same call stack, and + * performs them in the next micro task tick so that similar requests can be + * batched in a single RPC. + * + * For now, only 'read' calls are supported. + * + * @private + * @param {Object} params + * @returns {Promise} + */ + _performRPC: function (params) { + // save the RPC request + var def = $.Deferred(); + var request = _.extend({}, params, {def: def}); + this.batchedRPCsRequests.push(request); + + if (this.disableBatchedRPCs) { + this._performBatchedRPCs(); + } else { + // empty the pool of RPC requests in the next tick + setTimeout(this._performBatchedRPCs.bind(this)); + } + + return def; + }, /** * Reads data from server for all missing fields. * @@ -3976,11 +4070,13 @@ var BasicModel = AbstractModel.extend({ var def; if (missingIDs.length && fieldNames.length) { - def = self._rpc({ - model: list.model, - method: 'read', - args: [missingIDs, fieldNames], + // FORWARDPORT THIS UP TO 12.2, NOT FURTHER + def = self._performRPC({ context: list.getContext(), + fieldNames: fieldNames, + ids: missingIDs, + method: 'read', + model: list.model, }); } else { def = $.when(_.map(missingIDs, function (id) { -- cgit v1.2.3 From 7e889a1bd59c1cd177d376f73f5060568b02705b Mon Sep 17 00:00:00 2001 From: "Lucas Perais (lpe)" Date: Wed, 31 Jul 2019 15:19:44 +0000 Subject: [FIX] web: init makeRecord fields with correct values In the calendar view In the partner filter in the calendar side bar type something, and backspace rapidly to remove everything Before this commit, there was a error because the framework tried to write something in the corresponding field This was because the o2m field was not initialized with the correct values After this commit, there is no error OPW 2043975 closes odoo/odoo#35330 Signed-off-by: Lucas Perais (lpe) --- addons/web/static/src/js/views/basic/basic_model.js | 1 + 1 file changed, 1 insertion(+) (limited to 'addons/web/static/src/js/views/basic/basic_model.js') diff --git a/addons/web/static/src/js/views/basic/basic_model.js b/addons/web/static/src/js/views/basic/basic_model.js index 1a4f1574ac8..5e4ec6a4a6d 100644 --- a/addons/web/static/src/js/views/basic/basic_model.js +++ b/addons/web/static/src/js/views/basic/basic_model.js @@ -726,6 +726,7 @@ var BasicModel = AbstractModel.extend({ }); _.each(fields, function (field) { var dataPoint; + record.data[field.name] = null; if (field.type === 'many2one') { if (field.value) { var id = _.isArray(field.value) ? field.value[0] : field.value; -- cgit v1.2.3