summaryrefslogtreecommitdiff
path: root/addons/web/static/src/js/views/basic
diff options
context:
space:
mode:
authorGéry Debongnie <ged@odoo.com>2017-03-28 17:29:35 +0200
committerAaron Bohy <aab@odoo.com>2017-04-11 19:44:38 +0200
commite3f84e592ea5b734201d0532253fe0790eab49a6 (patch)
treef87ca471e5c2627272acc9a287a439ee3ccc1791 /addons/web/static/src/js/views/basic
parentef52914ea2b73b5f33c1020a823085ec82198c09 (diff)
[REF] web: use a mutex to serialize all operations in basic model
Diffstat (limited to 'addons/web/static/src/js/views/basic')
-rw-r--r--addons/web/static/src/js/views/basic/basic_model.js303
1 files changed, 156 insertions, 147 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 e3455658d50..b2c061f49e7 100644
--- a/addons/web/static/src/js/views/basic/basic_model.js
+++ b/addons/web/static/src/js/views/basic/basic_model.js
@@ -652,50 +652,7 @@ var BasicModel = AbstractModel.extend({
* @returns {string[]} list of changed fields
*/
notifyChanges: function (record_id, changes) {
- var self = this;
- var record = this.localData[record_id];
- var onChangeFields = []; // the fields that have changed and that have an on_change
- var field;
- var defs = [];
- record._changes = record._changes || {};
-
- // apply changes to local data
- for (var fieldName in changes) {
- field = record.fields[fieldName];
- if (field.type === 'one2many' || field.type === 'many2many') {
- defs.push(this._applyX2ManyChange(record, fieldName, changes[fieldName]));
- } else if (field.type === 'many2one') {
- defs.push(this._applyMany2OneChange(record, fieldName, changes[fieldName]));
- } else {
- record._changes[fieldName] = changes[fieldName];
- }
- if (field.onChange) {
- onChangeFields.push(fieldName);
- }
- }
-
- return $.when.apply($, defs).then(function () {
- var onchangeDef;
- if (onChangeFields.length) {
- onchangeDef = self._applyOnChange(record, onChangeFields).then(function (result) {
- return _.keys(changes).concat(Object.keys(result && result.value || {}));
- });
- } else {
- onchangeDef = $.Deferred().resolve(_.keys(changes));
- }
- return onchangeDef.then(function (fieldNames) {
- _.each(fieldNames, function (name) {
- if (record._changes && record._changes[name] === record.data[name]) {
- delete record._changes[name];
- }
- });
- return self._fetchSpecialData(record).then(function (fieldNames2) {
- // Return the names of the fields that changed (onchange or
- // associated special data change)
- return _.union(fieldNames, fieldNames2);
- });
- });
- });
+ return this.mutex.exec(this._applyChange.bind(this, record_id, changes));
},
/**
* Reload all data for a given resource
@@ -769,18 +726,17 @@ var BasicModel = AbstractModel.extend({
*/
save: function (record_id, options) {
var self = this;
- options = options || {};
- if (options.localSave) {
- var record = self.localData[record_id];
- if (record._changes) {
- _.extend(record.data, record._changes);
- record._changes = null;
- }
- return $.when();
- }
- var shouldReload = 'reload' in options ? options.reload : true;
return this.mutex.exec(function () {
+ options = options || {};
var record = self.localData[record_id];
+ if (options.localSave) {
+ if (record._changes) {
+ _.extend(record.data, record._changes);
+ record._changes = null;
+ }
+ return $.when();
+ }
+ var shouldReload = 'reload' in options ? options.reload : true;
var method = self.isNew(record_id) ? 'create' : 'write';
var changes = _.extend({}, record._changes);
var commands = self._generateX2ManyCommands(record);
@@ -958,6 +914,61 @@ var BasicModel = AbstractModel.extend({
//--------------------------------------------------------------------------
/**
+ * This method is the private version of notifyChanges. Unlike
+ * notifyChanges, it is not protected by a mutex. Every changes from the
+ * user to the model go through this method.
+ *
+ * @param {string} recordID
+ * @param {Object} changes
+ * @returns {Deferred}
+ */
+ _applyChange: function (recordID, changes) {
+ var self = this;
+ var record = this.localData[recordID];
+ var onChangeFields = []; // the fields that have changed and that have an on_change
+ var field;
+ var defs = [];
+ record._changes = record._changes || {};
+
+ // apply changes to local data
+ for (var fieldName in changes) {
+ field = record.fields[fieldName];
+ if (field.type === 'one2many' || field.type === 'many2many') {
+ defs.push(this._applyX2ManyChange(record, fieldName, changes[fieldName]));
+ } else if (field.type === 'many2one') {
+ defs.push(this._applyMany2OneChange(record, fieldName, changes[fieldName]));
+ } else {
+ record._changes[fieldName] = changes[fieldName];
+ }
+ if (field.onChange) {
+ onChangeFields.push(fieldName);
+ }
+ }
+
+ return $.when.apply($, defs).then(function () {
+ var onchangeDef;
+ if (onChangeFields.length) {
+ onchangeDef = self._applyOnChange(record, onChangeFields).then(function (result) {
+ return _.keys(changes).concat(Object.keys(result && result.value || {}));
+ });
+ } else {
+ onchangeDef = $.Deferred().resolve(_.keys(changes));
+ }
+ return onchangeDef.then(function (fieldNames) {
+ _.each(fieldNames, function (name) {
+ if (record._changes && record._changes[name] === record.data[name]) {
+ delete record._changes[name];
+ }
+ });
+ return self._fetchSpecialData(record).then(function (fieldNames2) {
+ // Return the names of the fields that changed (onchange or
+ // associated special data change)
+ return _.union(fieldNames, fieldNames2);
+ });
+ });
+ });
+ },
+ /**
* 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
@@ -1032,102 +1043,100 @@ var BasicModel = AbstractModel.extend({
if (fields.length === 1) {
fields = fields[0];
}
- return this.mutex.exec(function () {
- return self._rpc({
- model: record.model,
- method: 'onchange',
- args: [idList, currentData, fields, onchange_spec, context],
- })
- .then(function (result) {
- if (result.warning) {
- self.trigger_up('warning', {
- message: result.warning.message,
- title: result.warning.title,
- type: 'dialog',
- });
- }
- var defs = [];
- _.each(result.value, function (val, name) {
- var field = record.fields[name];
- if (!field) { return; } // ignore changes of unknown fields
-
- var rec;
- if (field.type === 'many2one' ) {
- // in some case, the value returned by the onchange can
- // be false (no value), so we need to avoid creating a
- // local record for that.
- // FIXME: shouldn't we erase the value in that case?
- if (val) {
- // when the value isn't false, it can be either
- // an array [id, display_name] or just an id.
- var data = _.isArray(val) ?
- {id: val[0], display_name: val[1]} :
- {id: val};
- rec = self._makeDataPoint({
+ return self._rpc({
+ model: record.model,
+ method: 'onchange',
+ args: [idList, currentData, fields, onchange_spec, context],
+ })
+ .then(function (result) {
+ if (result.warning) {
+ self.trigger_up('warning', {
+ message: result.warning.message,
+ title: result.warning.title,
+ type: 'dialog',
+ });
+ }
+ var defs = [];
+ _.each(result.value, function (val, name) {
+ var field = record.fields[name];
+ if (!field) { return; } // ignore changes of unknown fields
+
+ var rec;
+ if (field.type === 'many2one' ) {
+ // in some case, the value returned by the onchange can
+ // be false (no value), so we need to avoid creating a
+ // local record for that.
+ // FIXME: shouldn't we erase the value in that case?
+ if (val) {
+ // when the value isn't false, it can be either
+ // an array [id, display_name] or just an id.
+ var data = _.isArray(val) ?
+ {id: val[0], display_name: val[1]} :
+ {id: val};
+ rec = self._makeDataPoint({
+ context: context,
+ data: data,
+ modelName: field.relation,
+ });
+ record._changes[name] = rec.id;
+ }
+ } else if (field.type === 'one2many' || field.type === 'many2many') {
+ record._changes = record._changes || {};
+ var listId = record._changes[name] || record.data[name];
+ var list = self.localData[listId];
+ _.each(val, function (command) {
+ if (command[0] === 0 || command[0] === 1) {
+ // CREATE or UPDATE
+ var params = {
context: context,
- data: data,
- modelName: field.relation,
- });
- record._changes[name] = rec.id;
- }
- } else if (field.type === 'one2many' || field.type === 'many2many') {
- record._changes = record._changes || {};
- var listId = record._changes[name] || record.data[name];
- var list = self.localData[listId];
- _.each(val, function (command) {
- if (command[0] === 0 || command[0] === 1) {
- // CREATE or UPDATE
- var params = {
- context: context,
- fields: list.fields,
- fieldsInfo: list.fieldsInfo,
- modelName: list.model,
- fieldNames: _.keys(command[2]),
- };
- if (command[0] === 1) {
- params.res_id = command[1];
- }
- rec = self._makeDataPoint(params);
- var data = command[2];
- _.each(rec.fieldNames, function (name) {
- var field = rec.fields[name];
- if (field.type === 'many2one') {
- var r = self._makeDataPoint({
- modelName: field.relation,
- data: {
- id: data[name][0],
- display_name: data[name][1],
- },
- });
- data[name] = r.id;
- }
- });
- rec._changes = data;
- for (var f in rec._changes) {
- rec.data[f] = null;
- }
- list._changes.push(rec.id);
+ fields: list.fields,
+ fieldsInfo: list.fieldsInfo,
+ modelName: list.model,
+ fieldNames: _.keys(command[2]),
+ };
+ if (command[0] === 1) {
+ params.res_id = command[1];
}
- if (command[0] === 5) {
- // DELETE ALL
- list._changes = [];
+ rec = self._makeDataPoint(params);
+ var data = command[2];
+ _.each(rec.fieldNames, function (name) {
+ var field = rec.fields[name];
+ if (field.type === 'many2one') {
+ var r = self._makeDataPoint({
+ modelName: field.relation,
+ data: {
+ id: data[name][0],
+ display_name: data[name][1],
+ },
+ });
+ data[name] = r.id;
+ }
+ });
+ rec._changes = data;
+ for (var f in rec._changes) {
+ rec.data[f] = null;
}
- });
- } else if (field.type === 'date') {
- // process data: convert into a moment instance
- record._changes[name] = fieldUtils.parse.date(val);
- } else if (field.type === 'datetime') {
- // process datetime: convert into a moment instance
- record._changes[name] = fieldUtils.parse.datetime(val);
- } else {
- record._changes[name] = val;
- }
- });
- return $.when.apply($, defs).then(function () {
- return result;
- });
+ list._changes.push(rec.id);
+ }
+ if (command[0] === 5) {
+ // DELETE ALL
+ list._changes = [];
+ }
+ });
+ } else if (field.type === 'date') {
+ // process data: convert into a moment instance
+ record._changes[name] = fieldUtils.parse.date(val);
+ } else if (field.type === 'datetime') {
+ // process datetime: convert into a moment instance
+ record._changes[name] = fieldUtils.parse.datetime(val);
+ } else {
+ record._changes[name] = val;
+ }
});
- });
+ return $.when.apply($, defs).then(function () {
+ return result;
+ });
+ });
},
/**
* When an operation is applied to a x2many field, the field widgets
@@ -1206,7 +1215,7 @@ var BasicModel = AbstractModel.extend({
defs.push(this.addDefaultRecord(list.id));
break;
case 'UPDATE':
- defs.push(this.notifyChanges(command.id, command.data));
+ defs.push(this._applyChange(command.id, command.data));
break;
case 'REMOVE':
list._changes = _.without(list._changes, command.id);