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);