summaryrefslogtreecommitdiff
path: root/addons/web/static/src/js/views/basic/basic_model.js
diff options
context:
space:
mode:
authorMartin Geubelle <mge@odoo.com>2017-06-26 18:11:56 +0200
committerMartin Geubelle <mge@odoo.com>2017-06-28 16:53:45 +0200
commitec3bc2eeaf88c957340615ed0da71ac7b91a596b (patch)
tree292de80755badf3e1b4aa3a7c4851a1e31d389f1 /addons/web/static/src/js/views/basic/basic_model.js
parent157732cfb15d743c66c36a1bdcb350026a0473e4 (diff)
[FIX] web: deal with reference fields
The reference fields haven't been correctly implemented with the new views. This commit restores their behaviour. Note that an small improvement has been introduced in this commit. Before saas-16, a `name_get` was done for each record (so 80 if the field was displayed in a full list view), which is not optimal. The calls are now batched by model (so one `name_get` by model appearing in the field values).
Diffstat (limited to 'addons/web/static/src/js/views/basic/basic_model.js')
-rw-r--r--addons/web/static/src/js/views/basic/basic_model.js195
1 files changed, 176 insertions, 19 deletions
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 811e3632975..90fed121f15 100644
--- a/addons/web/static/src/js/views/basic/basic_model.js
+++ b/addons/web/static/src/js/views/basic/basic_model.js
@@ -332,8 +332,16 @@ var BasicModel = AbstractModel.extend({
} else {
data[fieldName] = this.get(data[fieldName]) || false;
}
- }
- if (field.type === 'one2many' || field.type === 'many2many') {
+ } else if (field.type === 'reference') {
+ if (options.raw) {
+ relDataPoint = this.localData[data[fieldName]];
+ data[fieldName] = relDataPoint ?
+ relDataPoint.model + ',' + relDataPoint.res_id :
+ false;
+ } else {
+ data[fieldName] = this.get(data[fieldName]) || false;
+ }
+ } else if (field.type === 'one2many' || field.type === 'many2many') {
if (options.raw) {
relDataPoint = this.localData[data[fieldName]];
relDataPoint = this._applyX2ManyOperations(relDataPoint);
@@ -946,8 +954,8 @@ var BasicModel = AbstractModel.extend({
field = record.fields[fieldName];
if (field.type === 'one2many' || field.type === 'many2many') {
defs.push(this._applyX2ManyChange(record, fieldName, changes[fieldName], viewType));
- } else if (field.type === 'many2one') {
- defs.push(this._applyMany2OneChange(record, fieldName, changes[fieldName]));
+ } else if (field.type === 'many2one' || field.type === 'reference') {
+ defs.push(this._applyX2OneChange(record, fieldName, changes[fieldName]));
} else {
record._changes[fieldName] = changes[fieldName];
}
@@ -989,19 +997,19 @@ var BasicModel = AbstractModel.extend({
});
},
/**
- * Apply a many2one onchange. There is a need for this function because the
- * server only gives an id when a onchange modifies a many2one field. For
- * this reason, we need (sometimes) to do a /name_get to fetch a
- * display_name.
+ * Apply an x2one (either a many2one or a reference field) change. There is
+ * a need for this function because the server only gives an id when a
+ * onchange modifies a many2one field. For this reason, we need (sometimes)
+ * to do a /name_get to fetch a display_name.
*
* @param {Object} record
* @param {string} fieldName
* @param {Object} [data]
* @returns {Deferred}
*/
- _applyMany2OneChange: function (record, fieldName, data) {
+ _applyX2OneChange: function (record, fieldName, data) {
var self = this;
- if (!data || data.id === false) {
+ if (!data || !data.id) {
record._changes[fieldName] = false;
return $.when();
}
@@ -1022,12 +1030,16 @@ var BasicModel = AbstractModel.extend({
return $.when();
}
var rel_data = _.pick(data, 'id', 'display_name');
+ var field = record.fields[fieldName];
+
+ // the reference field doesn't store its co-model in its field metadata
+ // but directly in the data (as the co-model isn't fixed)
+ var coModel = field.type === 'reference' ? data.model : field.relation;
var def;
if (rel_data.display_name === undefined) {
- var field = record.fields[fieldName];
// TODO: refactor this to use _fetchNameGet
def = this._rpc({
- model: field.relation,
+ model: coModel,
method: 'name_get',
args: [data.id],
context: record.context,
@@ -1042,7 +1054,7 @@ var BasicModel = AbstractModel.extend({
data: rel_data,
fields: {},
fieldsInfo: {},
- modelName: record.fields[fieldName].relation,
+ modelName: coModel,
parentID: record.id,
});
record._changes[fieldName] = rec.id;
@@ -1239,7 +1251,10 @@ var BasicModel = AbstractModel.extend({
list_records[record.id].data = record;
self._parseServerData(fieldNames, list, record);
});
- return self._fetchX2ManysBatched(list);
+ return $.when(
+ self._fetchX2ManysBatched(list),
+ self._fetchReferencesBatched(list)
+ );
});
defs.push(def);
}
@@ -1544,12 +1559,140 @@ var BasicModel = AbstractModel.extend({
self._parseServerData(fieldNames, record, record.data);
})
.then(function () {
- return self._fetchX2Manys(record, options).then(function () {
+ return $.when(
+ self._fetchX2Manys(record, options),
+ self._fetchReferences(record)
+ ).then(function () {
return self._postprocess(record, options);
});
});
},
/**
+ * Fetch the extra data (`name_get`) for the reference fields of the record
+ * model.
+ *
+ * @private
+ * @param {Object} record
+ * @returns {Deferred}
+ */
+ _fetchReferences: function (record) {
+ var self = this;
+ var defs = [];
+ var fieldNames = record.getFieldNames();
+ _.each(fieldNames, function (fieldName) {
+ var field = record.fields[fieldName];
+ if (field.type === 'reference') {
+ var value = record.data[fieldName];
+ if (value) {
+ var model = value.split(',')[0];
+ var resID = parseInt(value.split(',')[1]);
+ var def = self._rpc({
+ model: model,
+ method: 'name_get',
+ args: [resID],
+ context: record.getContext({fieldName: fieldName}),
+ }).then(function (result) {
+ var referenceDp = self._makeDataPoint({
+ data: {
+ id: result[0][0],
+ display_name: result[0][1],
+ },
+ modelName: model,
+ parentID: record.id,
+ });
+ record.data[fieldName] = referenceDp.id;
+ });
+ defs.push(def);
+ }
+ }
+ });
+ return $.when.apply($, defs);
+ },
+ /**
+ * Batch requests for one reference field in list (one request by different
+ * model in the field values).
+ *
+ * @see _fetchReferencesBatched
+ * @param {Object} list
+ * @param {string} fieldName
+ * @returns {Deferred}
+ */
+ _fetchReferenceBatched: function (list, fieldName) {
+ var self = this;
+ list = this._applyX2ManyOperations(list);
+
+ // collect ids by model
+ var toFetch = {};
+ _.each(list.data, function (dataPoint) {
+ var record = self.localData[dataPoint];
+ var value = record.data[fieldName];
+ if (value) {
+ var model = value.split(',')[0];
+ var resID = value.split(',')[1];
+ if (!(model in toFetch)) {
+ toFetch[model] = {};
+ }
+ // there could be multiple datapoints with the same model/resID
+ if (toFetch[model][resID]) {
+ toFetch[model][resID].push(dataPoint);
+ } else {
+ toFetch[model][resID] = [dataPoint];
+ }
+ }
+ });
+
+ var defs = [];
+ var def;
+ // one name_get by model
+ _.each(toFetch, function (datapoints, model) {
+ var ids = _.map(Object.keys(datapoints), function (id) { return parseInt(id); });
+ // we need one parent for the context (they all have the same)
+ var parent = datapoints[ids[0]][0];
+ def = self._rpc({
+ model: model,
+ method: 'name_get',
+ args: [ids],
+ context: self.localData[parent].getContext({fieldName: fieldName}),
+ }).then(function (result) {
+ _.each(result, function (el) {
+ var parentIDs = datapoints[el[0]];
+ _.each(parentIDs, function (parentID) {
+ var parent = self.localData[parentID];
+ var referenceDp = self._makeDataPoint({
+ data: {
+ id: el[0],
+ display_name: el[1],
+ },
+ modelName: model,
+ parentID: parent,
+ });
+ parent.data[fieldName] = referenceDp.id;
+ });
+ });
+ });
+ defs.push(def);
+ });
+
+ return $.when.apply($, defs);
+ },
+ /**
+ * Batch requests for references for datapoint of type list.
+ *
+ * @param {Object} list
+ * @returns {Deferred}
+ */
+ _fetchReferencesBatched: function (list) {
+ var defs = [];
+ var fieldNames = list.getFieldNames();
+ for (var i = 0; i < fieldNames.length; i++) {
+ var field = list.fields[fieldNames[i]];
+ if (field.type === 'reference') {
+ defs.push(this._fetchReferenceBatched(list, fieldNames[i]));
+ }
+ }
+ return $.when.apply($, defs);
+ },
+ /**
* This method is incorrectly named. It should be named something like
* _fetchMany2OneData.
*
@@ -1826,7 +1969,9 @@ var BasicModel = AbstractModel.extend({
def = this._searchReadUngroupedList(list);
}
return def.then(function () {
- return self._fetchX2ManysBatched(list);
+ return $.when(
+ self._fetchX2ManysBatched(list),
+ self._fetchReferencesBatched(list));
}).then(function () {
return list;
});
@@ -1880,7 +2025,10 @@ var BasicModel = AbstractModel.extend({
record.data[fieldName] = list.id;
if (!fieldInfo.__no_fetch) {
var def = self._readUngroupedList(list).then(function () {
- return self._fetchX2ManysBatched(list);
+ return $.when(
+ self._fetchX2ManysBatched(list),
+ self._fetchReferencesBatched(list)
+ );
});
defs.push(def);
}
@@ -2029,6 +2177,7 @@ var BasicModel = AbstractModel.extend({
// process relational fields and handle the null case
var type = record.fields[fieldName].type;
+ var value;
if (type === 'one2many' || type === 'many2many') {
if (commands[fieldName].length) { // replace localId by commands
changes[fieldName] = commands[fieldName];
@@ -2036,8 +2185,13 @@ var BasicModel = AbstractModel.extend({
delete changes[fieldName];
}
} else if (type === 'many2one' && fieldName in changes) {
- var value = changes[fieldName];
+ value = changes[fieldName];
changes[fieldName] = value ? this.localData[value].res_id : false;
+ } else if (type === 'reference' && fieldName in changes) {
+ value = changes[fieldName];
+ changes[fieldName] = value ?
+ this.localData[value].model + ',' + this.localData[value].res_id :
+ false;
} else if (changes[fieldName] === null) {
changes[fieldName] = false;
}
@@ -2683,7 +2837,10 @@ var BasicModel = AbstractModel.extend({
x2manyList.res_ids = value[2];
x2manyList.count = x2manyList.res_ids.length;
var def = self._readUngroupedList(x2manyList).then(function () {
- return self._fetchX2ManysBatched(x2manyList);
+ return $.when(
+ self._fetchX2ManysBatched(x2manyList),
+ self._fetchReferencesBatched(x2manyList)
+ );
});
defs.push(def);
}