From e9dce2c7b655d0f0b2b1075c05a37a190f1f6473 Mon Sep 17 00:00:00 2001 From: Aaron Bohy Date: Fri, 28 Apr 2017 12:29:22 +0200 Subject: [PATCH] [FIX] web: don't fetch invisible relational fields Relational fields with attribute 'invisible' set to true are in views because they are used in domains or contexts. In this case their relational data is useless, only their ids matter. Before this rev., their relational data was fetched anyway. More precisely, it was the case for many2ones after a default_get, and for x2manys with inline views, or without view (e.g. with widget 'many2many_tags'). --- .../static/src/js/services/data_manager.js | 18 ++++++ .../static/src/js/views/basic/basic_model.js | 2 +- .../web/static/tests/helpers/mock_server.js | 2 +- addons/web/static/tests/views/form_tests.js | 56 +++++++++++++++++++ addons/web/static/tests/views/list_tests.js | 25 +++++++++ 5 files changed, 101 insertions(+), 2 deletions(-) diff --git a/addons/web/static/src/js/services/data_manager.js b/addons/web/static/src/js/services/data_manager.js index cccad3ddc3f..4786515bb15 100644 --- a/addons/web/static/src/js/services/data_manager.js +++ b/addons/web/static/src/js/services/data_manager.js @@ -274,6 +274,24 @@ return core.Class.extend({ field.onChange = "1"; } + // the relational data of invisible relational fields should not be + // fetched (e.g. name_gets of invisible many2ones), at least those that + // are always invisible. + // the invisible attribute of a field is supposed to be static ("1" in + // general), but not totally as it may use keys of the context + // ("context.get('some_key')"). It is evaluated server-side, and the + // result is put inside the modifiers as a value of the '(tree_)invisible' + // key, and the raw value is left in the invisible attribute (it is used + // in debug mode for informational purposes). + // this should change, for instance the server might set the evaluated + // value in invisible, which could then be seen as static by the client, + // and add another key in debug mode containing the raw value. + // for now, we do an hack to detect if the value is static and retrieve + // it from the modifiers, + if (attrs.invisible && attrs.modifiers.match('"(?:tree_)?invisible": ?true')) { + attrs.__no_fetch = true; + } + if (!_.isEmpty(field.views)) { // process the inner fields_view as well to find the fields they use. // register those fields' description directly on the view. 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 f3e69a6b453..9bd33b90a3d 100644 --- a/addons/web/static/src/js/views/basic/basic_model.js +++ b/addons/web/static/src/js/views/basic/basic_model.js @@ -1319,7 +1319,7 @@ var BasicModel = AbstractModel.extend({ // find all many2one related records to be fetched _.each(record.getFieldNames(), function (name) { var field = record.fields[name]; - if (field.type === 'many2one') { + if (field.type === 'many2one' && !record.fieldsInfo[record.viewType][name].__no_fetch) { var localId = (record._changes && record._changes[name]) || record.data[name]; var relatedRecord = self.localData[localId]; if (!relatedRecord) { diff --git a/addons/web/static/tests/helpers/mock_server.js b/addons/web/static/tests/helpers/mock_server.js index f7faf2efb8f..cbd1f1b1e1f 100644 --- a/addons/web/static/tests/helpers/mock_server.js +++ b/addons/web/static/tests/helpers/mock_server.js @@ -226,7 +226,7 @@ var MockServer = Class.extend({ } _.each(modifiersNames, function (a) { if (node.attrs[a]) { - var v = pyeval.py_eval(node.attrs[a]); + var v = pyeval.py_eval(node.attrs[a]) ? true: false; if (inTreeView && a === 'invisible') { modifiers['tree_invisible'] = v; } else if (v || !(a in modifiers) || !_.isArray(modifiers[a])) { diff --git a/addons/web/static/tests/views/form_tests.js b/addons/web/static/tests/views/form_tests.js index 69d1d8c1993..4b6f43add2b 100644 --- a/addons/web/static/tests/views/form_tests.js +++ b/addons/web/static/tests/views/form_tests.js @@ -3819,5 +3819,61 @@ QUnit.module('Views', { form.destroy(); }); + QUnit.test('do not perform extra RPC to read invisible many2one fields', function (assert) { + assert.expect(2); + + this.data.partner.fields.trululu.default = 2; + + var form = createView({ + View: FormView, + model: 'partner', + data: this.data, + arch: '
' + + '' + + '' + + '' + + '
', + mockRPC: function (route, args) { + assert.step(args.method); + return this._super.apply(this, arguments); + }, + }); + + assert.verifySteps(['default_get'], "only one RPC should have been done"); + + form.destroy(); + }); + + QUnit.test('do not perform extra RPC to read invisible x2many fields', function (assert) { + assert.expect(2); + + this.data.partner.records[0].p = [2]; // one2many + this.data.partner.records[0].product_ids = [37]; // one2many + this.data.partner.records[0].timmy = [12]; // many2many + + var form = createView({ + View: FormView, + model: 'partner', + data: this.data, + arch: '
' + + '' + + '' + // no inline view + '' + // inline view + '' + + '' + + '' + // no view + '' + + '
', + mockRPC: function (route, args) { + assert.step(args.method); + return this._super.apply(this, arguments); + }, + res_id: 1, + }); + + assert.verifySteps(['read'], "only one read should have been done"); + + form.destroy(); + }); }); }); diff --git a/addons/web/static/tests/views/list_tests.js b/addons/web/static/tests/views/list_tests.js index a31620a4cd3..9ad2f10e631 100644 --- a/addons/web/static/tests/views/list_tests.js +++ b/addons/web/static/tests/views/list_tests.js @@ -164,6 +164,31 @@ QUnit.module('Views', { list.destroy(); }); + QUnit.test('do not perform extra RPC to read invisible many2one fields', function (assert) { + assert.expect(3); + + this.data.foo.fields.m2o.default = 2; + + var list = createView({ + View: ListView, + model: 'foo', + data: this.data, + arch: '' + + '' + + '' + + '', + mockRPC: function (route, args) { + assert.step(_.last(route.split('/'))); + return this._super.apply(this, arguments); + }, + }); + + list.$buttons.find('.o_list_button_add').click(); + assert.verifySteps(['search_read', 'default_get'], "no nameget should be done"); + + list.destroy(); + }); + QUnit.test('at least 4 rows are rendered, even if less data', function (assert) { assert.expect(1);