[IMP] web: BasicModel: improve the performance of context eval
Before this commit, the evaluation of the context was done for all the context, even when only a subset of the context was needed. Now, we evaluate only the attributes of the context that are used. Part-of: odoo/odoo#78221
This commit is contained in:
committed by
Géry Debongnie
parent
5329b9a9ae
commit
6994ccb31f
@@ -530,7 +530,7 @@ var BasicModel = AbstractModel.extend({
|
||||
}
|
||||
|
||||
if (element.type === 'record') {
|
||||
var data = _.extend({}, element.data, element._changes);
|
||||
var data = Object.assign({}, element.data, element._changes);
|
||||
var relDataPoint;
|
||||
for (var fieldName in data) {
|
||||
var field = element.fields[fieldName];
|
||||
@@ -576,7 +576,7 @@ var BasicModel = AbstractModel.extend({
|
||||
}
|
||||
}
|
||||
var record = {
|
||||
context: _.extend({}, element.context),
|
||||
context: Object.assign({}, element.context),
|
||||
count: element.count,
|
||||
data: data,
|
||||
domain: element.domain.slice(0),
|
||||
@@ -594,7 +594,7 @@ var BasicModel = AbstractModel.extend({
|
||||
offset: element.offset,
|
||||
ref: element.ref,
|
||||
res_ids: element.res_ids.slice(0),
|
||||
specialData: _.extend({}, element.specialData),
|
||||
specialData: Object.assign({}, element.specialData),
|
||||
type: 'record',
|
||||
viewType: element.viewType,
|
||||
};
|
||||
@@ -2208,7 +2208,7 @@ var BasicModel = AbstractModel.extend({
|
||||
return list;
|
||||
}
|
||||
var self = this;
|
||||
list = _.extend({}, list);
|
||||
list = Object.assign({}, list);
|
||||
list.res_ids = list.res_ids.slice(0);
|
||||
var changes = list._changes || [];
|
||||
if (options) {
|
||||
@@ -3486,7 +3486,6 @@ var BasicModel = AbstractModel.extend({
|
||||
_getContext: function (element, options) {
|
||||
options = options || {};
|
||||
var context = new Context(session.user_context);
|
||||
context.set_eval_context(this._getEvalContext(element));
|
||||
|
||||
if (options.full || !(options.fieldName || options.additionalContext)) {
|
||||
var context_to_add = options.sanitize_default_values ?
|
||||
@@ -3500,7 +3499,9 @@ var BasicModel = AbstractModel.extend({
|
||||
var viewType = options.viewType || element.viewType;
|
||||
var fieldInfo = element.fieldsInfo[viewType][options.fieldName];
|
||||
if (fieldInfo && fieldInfo.context) {
|
||||
context.add(fieldInfo.context);
|
||||
if (fieldInfo.context !== "{}") {
|
||||
context.add(fieldInfo.context);
|
||||
}
|
||||
} else {
|
||||
var fieldParams = element.fields[options.fieldName];
|
||||
if (fieldParams.context) {
|
||||
@@ -3519,7 +3520,12 @@ var BasicModel = AbstractModel.extend({
|
||||
context.add(options.additionalContext);
|
||||
}
|
||||
|
||||
return context.eval();
|
||||
if (context.__contexts.length > 1) {
|
||||
context.set_eval_context(this._getEvalContext(element));
|
||||
return context.eval();
|
||||
} else {
|
||||
return Object.assign({}, session.user_context);
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Collects from a record a list of ids to fetch, according to fieldName,
|
||||
@@ -3729,6 +3735,66 @@ var BasicModel = AbstractModel.extend({
|
||||
evalContext,
|
||||
);
|
||||
},
|
||||
|
||||
_getLazyEvalContext: function (element, forDomain) {
|
||||
var evalContext = element.type === 'record' ? this._getLazyRecordEvalContext(element, forDomain) : {};
|
||||
|
||||
if (element.parentID) {
|
||||
var parent = this.localData[element.parentID];
|
||||
if (parent.type === 'list' && parent.parentID) {
|
||||
parent = this.localData[parent.parentID];
|
||||
}
|
||||
if (parent.type === 'record') {
|
||||
evalContext.parent = this._getLazyRecordEvalContext(parent, forDomain);
|
||||
}
|
||||
}
|
||||
// Uses "current_company_id" because "company_id" would conflict with all the company_id fields
|
||||
// in general, the actual "company_id" field of the form should be used for m2o domains, not this fallback
|
||||
let current_company_id;
|
||||
if (session.user_context.allowed_company_ids) {
|
||||
current_company_id = session.user_context.allowed_company_ids[0];
|
||||
} else {
|
||||
current_company_id = session.user_companies ?
|
||||
session.user_companies.current_company :
|
||||
false;
|
||||
}
|
||||
let fallbackContext = Object.assign(
|
||||
{
|
||||
active_id: evalContext.id || false,
|
||||
active_ids: evalContext.id ? [evalContext.id] : [],
|
||||
active_model: element.model,
|
||||
current_company_id,
|
||||
id: evalContext.id || false,
|
||||
},
|
||||
pyUtils.context(),
|
||||
session.user_context,
|
||||
element.context,
|
||||
);
|
||||
|
||||
return new Proxy(
|
||||
{}, {
|
||||
has(target, key) {
|
||||
return key in target || key in evalContext || key in fallbackContext;
|
||||
},
|
||||
get (target, key) {
|
||||
switch (true) {
|
||||
case key in target:
|
||||
return target[key];
|
||||
case key in evalContext:
|
||||
return evalContext[key];
|
||||
default:
|
||||
return fallbackContext[key];;
|
||||
}
|
||||
},
|
||||
ownKeys(target) {
|
||||
return [...new Set([...Reflect.ownKeys(target), ...Object.keys(fallbackContext), ...Object.keys(evalContext)])];
|
||||
},
|
||||
getOwnPropertyDescriptor(target, prop) {
|
||||
return Reflect.getOwnPropertyDescriptor(target, prop) || { writable: true, configurable: true, enumerable: true };
|
||||
},
|
||||
}
|
||||
);
|
||||
},
|
||||
/**
|
||||
* Returns the list of field names of the given element according to its
|
||||
* default view type.
|
||||
@@ -3862,6 +3928,74 @@ var BasicModel = AbstractModel.extend({
|
||||
}
|
||||
return context;
|
||||
},
|
||||
|
||||
_getLazyRecordEvalContext: function (record, forDomain) {
|
||||
let self = this;
|
||||
let relDataPoint;
|
||||
|
||||
// calls _generateX2ManyCommands for a given field, and returns the array of commands
|
||||
function _generateX2ManyCommands(fieldName) {
|
||||
var commands = self._generateX2ManyCommands(record, {fieldNames: [fieldName]});
|
||||
return commands[fieldName];
|
||||
}
|
||||
|
||||
return new Proxy({}, {
|
||||
has(target, key) {
|
||||
return key in target || key in record.fields;
|
||||
},
|
||||
ownKeys(target) {
|
||||
return [...new Set([...Reflect.ownKeys(target), ...Object.keys(record.fields)])];
|
||||
},
|
||||
getOwnPropertyDescriptor(target, prop) {
|
||||
return Reflect.getOwnPropertyDescriptor(target, prop) || { writable: true, configurable: true, enumerable: true };
|
||||
},
|
||||
get( target, fieldName ) {
|
||||
if (fieldName in target){
|
||||
return target[fieldName];
|
||||
}
|
||||
const fieldValue = record._changes && fieldName in record._changes ? record._changes[fieldName] : record.data[fieldName];
|
||||
const field = record.fields[fieldName];
|
||||
if (fieldValue === null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (field.type === 'date' || field.type === 'datetime') {
|
||||
if (fieldValue) {
|
||||
return JSON.parse(JSON.stringify(fieldValue));
|
||||
}
|
||||
}
|
||||
if (field.type === 'many2one') {
|
||||
relDataPoint = self.localData[fieldValue];
|
||||
return relDataPoint ? relDataPoint.res_id : false;
|
||||
}
|
||||
if (field.type === 'one2many' || field.type === 'many2many') {
|
||||
let ids;
|
||||
if (!fieldValue || _.isArray(fieldValue)) { // no dataPoint created yet
|
||||
ids = fieldValue ? fieldValue.slice(0) : [];
|
||||
} else {
|
||||
relDataPoint = self._applyX2ManyOperations(self.localData[fieldValue]);
|
||||
ids = relDataPoint.res_ids.slice(0);
|
||||
}
|
||||
if (!forDomain) {
|
||||
// when sent to the server, the x2manys values must be a list
|
||||
// of commands in a context, but the list of ids in a domain
|
||||
ids.toJSON = _generateX2ManyCommands.bind(null, fieldName);
|
||||
} else if (field.type === 'one2many') { // Ids are evaluated as a list of ids
|
||||
/* Filtering out virtual ids from the ids list
|
||||
* The server will crash if there are virtual ids in there
|
||||
* The webClient doesn't do literal id list comparison like ids == list
|
||||
* Only relevant in o2m: m2m does create actual records in db
|
||||
*/
|
||||
ids = _.filter(ids, function (id) {
|
||||
return typeof id !== 'string';
|
||||
});
|
||||
}
|
||||
return ids;
|
||||
}
|
||||
return fieldValue;
|
||||
}
|
||||
});
|
||||
},
|
||||
/**
|
||||
* Get the domain for a list resource.
|
||||
*
|
||||
|
||||
@@ -211,16 +211,7 @@ class DataPoint {
|
||||
|
||||
get evalContext() {
|
||||
const datapoint = this.model.__bm__.localData[this.__bm_handle__];
|
||||
const evalContext = this.model.__bm__._getEvalContext(datapoint);
|
||||
// FIXME: in the basic model, we set the toJSON function on x2many values
|
||||
// s.t. we send commands to the server. In wowl Domain, we JSON.stringify
|
||||
// values to compare them, so it doesn't work as expected.
|
||||
for (const key in evalContext) {
|
||||
if (evalContext[key]) {
|
||||
delete evalContext[key].toJSON;
|
||||
}
|
||||
}
|
||||
return evalContext;
|
||||
return this.model.__bm__._getLazyEvalContext(datapoint);
|
||||
}
|
||||
|
||||
get fieldNames() {
|
||||
|
||||
Reference in New Issue
Block a user