2
0
mirror of https://github.com/frappe/books.git synced 2024-12-26 12:28:12 +00:00

fix: frappe.registerModels

- Directly register from models object, not models.models
- frappe.getModels
- Validate model before registering
- Fix model.extend bug for duplicate fields
- Remove usage of deepmerge and use lodash/cloneDeep
This commit is contained in:
Faris Ansari 2019-12-18 23:48:53 +05:30
parent 86c57af94b
commit 7198979fc7
6 changed files with 165 additions and 116 deletions

View File

@ -1,4 +1,5 @@
const Observable = require('./utils/observable'); const Observable = require('./utils/observable');
const utils = require('./utils');
module.exports = { module.exports = {
async init() { async init() {
@ -34,40 +35,36 @@ module.exports = {
common.initLibs(this); common.initLibs(this);
}, },
registerModels(models, type) { registerModels(models) {
// register models from app/models/index.js // register models from app/models/index.js
const toAdd = Object.assign({}, models.models); for (let doctype in models) {
let metaDefinition = models[doctype];
// post process based on type if (!metaDefinition.name) {
if (models[type]) { throw new Error(`Name is mandatory for ${doctype}`);
models[type](toAdd); }
if (metaDefinition.name !== doctype) {
throw new Error(
`Model name mismatch for ${doctype}: ${metaDefinition.name}`
);
}
let fieldnames = (metaDefinition.fields || []).map(df => df.fieldname).sort();
let duplicateFieldnames = utils.getDuplicates(fieldnames);
if (duplicateFieldnames.length > 0) {
throw new Error(
`Duplicate fields in ${doctype}: ${duplicateFieldnames.join(', ')}`
);
} }
Object.assign(this.models, toAdd); this.models[doctype] = metaDefinition;
}
}, },
getDoctypeList(filters) { getModels(filterFunction) {
let doctypeList = []; let models = [];
if (filters && Object.keys(filters).length) { for (let doctype in this.models) {
for (let model in this.models) { models.push(this.models[doctype]);
let doctypeName = model;
let doctype = this.models[doctypeName];
let matchedFields = 0;
for (let key in filters) {
let field = key;
let value = filters[field];
if (Boolean(doctype[field]) === Boolean(value)) {
matchedFields++;
} }
} return filterFunction ? models.filter(filterFunction) : models;
if (matchedFields === Object.keys(filters).length)
doctypeList.push(doctypeName);
}
}
return doctypeList;
}, },
registerView(view, name, module) { registerView(view, name, module) {

View File

@ -1,20 +1,10 @@
const deepmerge = require('deepmerge'); const cloneDeep = require('lodash/cloneDeep');
module.exports = { module.exports = {
extend: (base, target, options = {}) => { extend: (base, target, options = {}) => {
base = deepmerge({}, base); base = cloneDeep(base);
const fieldsToMerge = (target.fields || []).map(df => df.fieldname); const fieldsToMerge = (target.fields || []).map(df => df.fieldname);
const fieldsToRemove = options.skipFields || []; const fieldsToRemove = options.skipFields || [];
base.fields = base.fields
.filter(df => !fieldsToRemove.includes(df.fieldname))
.map(df => {
if (fieldsToMerge.includes(df.fieldname)) {
return deepmerge(df, target.fields.find(tdf => tdf.fieldname === df.fieldname));
}
return df;
});
const overrideProps = options.overrideProps || []; const overrideProps = options.overrideProps || [];
for (let prop of overrideProps) { for (let prop of overrideProps) {
if (base.hasOwnProperty(prop)) { if (base.hasOwnProperty(prop)) {
@ -22,50 +12,103 @@ module.exports = {
} }
} }
return deepmerge(base, target); let mergeFields = (baseFields, targetFields) => {
let fields = cloneDeep(baseFields);
fields = fields
.filter(df => !fieldsToRemove.includes(df.fieldname))
.map(df => {
if (fieldsToMerge.includes(df.fieldname)) {
let copy = cloneDeep(df);
return Object.assign(
copy,
targetFields.find(tdf => tdf.fieldname === df.fieldname)
);
}
return df;
});
let fieldsAdded = fields.map(df => df.fieldname);
let fieldsToAdd = targetFields.filter(
df => !fieldsAdded.includes(df.fieldname)
);
return fields.concat(fieldsToAdd);
};
let fields = mergeFields(base.fields, target.fields || []);
let out = Object.assign(base, target);
out.fields = fields;
return out;
}, },
commonFields: [ commonFields: [
{ {
fieldname: 'name', fieldtype: 'Data', required: 1 fieldname: 'name',
fieldtype: 'Data',
required: 1
}
],
submittableFields: [
{
fieldname: 'submitted',
fieldtype: 'Check',
required: 1
} }
], ],
parentFields: [ parentFields: [
{ {
fieldname: 'owner', fieldtype: 'Data', required: 1 fieldname: 'owner',
fieldtype: 'Data',
required: 1
}, },
{ {
fieldname: 'modifiedBy', fieldtype: 'Data', required: 1 fieldname: 'modifiedBy',
fieldtype: 'Data',
required: 1
}, },
{ {
fieldname: 'creation', fieldtype: 'Datetime', required: 1 fieldname: 'creation',
fieldtype: 'Datetime',
required: 1
}, },
{ {
fieldname: 'modified', fieldtype: 'Datetime', required: 1 fieldname: 'modified',
fieldtype: 'Datetime',
required: 1
}, },
{ {
fieldname: 'keywords', fieldtype: 'Text' fieldname: 'keywords',
fieldtype: 'Text'
} }
], ],
childFields: [ childFields: [
{ {
fieldname: 'idx', fieldtype: 'Int', required: 1 fieldname: 'idx',
fieldtype: 'Int',
required: 1
}, },
{ {
fieldname: 'parent', fieldtype: 'Data', required: 1 fieldname: 'parent',
fieldtype: 'Data',
required: 1
}, },
{ {
fieldname: 'parenttype', fieldtype: 'Data', required: 1 fieldname: 'parenttype',
fieldtype: 'Data',
required: 1
}, },
{ {
fieldname: 'parentfield', fieldtype: 'Data', required: 1 fieldname: 'parentfield',
fieldtype: 'Data',
required: 1
} }
], ],
treeFields: [ treeFields: [
{ {
fieldname: 'lft', fieldtype: 'Int' fieldname: 'lft',
fieldtype: 'Int'
}, },
{ {
fieldname: 'rgt', fieldtype: 'Int' fieldname: 'rgt',
fieldtype: 'Int'
} }
] ]
}; };

View File

@ -1,5 +1,4 @@
module.exports = { module.exports = {
models: {
FilterItem: require('./doctype/FilterItem/FilterItem.js'), FilterItem: require('./doctype/FilterItem/FilterItem.js'),
FilterGroup: require('./doctype/FilterGroup/FilterGroup.js'), FilterGroup: require('./doctype/FilterGroup/FilterGroup.js'),
FilterSelector: require('./doctype/FilterSelector/FilterSelector.js'), FilterSelector: require('./doctype/FilterSelector/FilterSelector.js'),
@ -12,6 +11,5 @@ module.exports = {
ToDo: require('./doctype/ToDo/ToDo.js'), ToDo: require('./doctype/ToDo/ToDo.js'),
User: require('./doctype/User/User.js'), User: require('./doctype/User/User.js'),
UserRole: require('./doctype/UserRole/UserRole.js'), UserRole: require('./doctype/UserRole/UserRole.js'),
File: require('./doctype/File/File.js'), File: require('./doctype/File/File.js')
} };
}

View File

@ -23,7 +23,6 @@
"cross-env": "^5.2.0", "cross-env": "^5.2.0",
"css-loader": "^1.0.0", "css-loader": "^1.0.0",
"csvjson-csv2json": "5.0.6", "csvjson-csv2json": "5.0.6",
"deepmerge": "^2.1.0",
"electron": "5.0.0", "electron": "5.0.0",
"electron-builder": "^21.0.15", "electron-builder": "^21.0.15",
"electron-debug": "^2.0.0", "electron-debug": "^2.0.0",
@ -34,6 +33,7 @@
"friendly-errors-webpack-plugin": "^1.7.0", "friendly-errors-webpack-plugin": "^1.7.0",
"html-webpack-plugin": "^3.2.0", "html-webpack-plugin": "^3.2.0",
"knex": "^0.20.3", "knex": "^0.20.3",
"lodash": "^4.17.15",
"luxon": "^1.0.0", "luxon": "^1.0.0",
"mkdirp": "^0.5.1", "mkdirp": "^0.5.1",
"morgan": "^1.9.0", "morgan": "^1.9.0",

View File

@ -81,6 +81,21 @@ function unique(list, key = it => it) {
}); });
} }
function getDuplicates(array) {
let duplicates = [];
for (let i in array) {
let previous = array[i - 1];
let current = array[i];
if (current === previous) {
if (!duplicates.includes(current)) {
duplicates.push(current);
}
}
}
return duplicates;
}
module.exports = { module.exports = {
_, _,
slug, slug,
@ -91,4 +106,5 @@ module.exports = {
asyncHandler, asyncHandler,
range, range,
unique, unique,
getDuplicates
} }

View File

@ -1931,11 +1931,6 @@ deep-extend@^0.6.0:
resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac" resolved "https://registry.yarnpkg.com/deep-extend/-/deep-extend-0.6.0.tgz#c4fa7c95404a17a9c3e8ca7e1537312b736330ac"
integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA== integrity sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==
deepmerge@^2.1.0:
version "2.2.1"
resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-2.2.1.tgz#5d3ff22a01c00f645405a2fbc17d0778a1801170"
integrity sha512-R9hc1Xa/NOBi9WRVUWg19rl1UB7Tt4kuPd+thNJgFZoxXsTz7ncaPaeIm+40oSGuP33DfMb4sZt1QIGiJzC4EA==
default-gateway@^4.2.0: default-gateway@^4.2.0:
version "4.2.0" version "4.2.0"
resolved "https://registry.yarnpkg.com/default-gateway/-/default-gateway-4.2.0.tgz#167104c7500c2115f6dd69b0a536bb8ed720552b" resolved "https://registry.yarnpkg.com/default-gateway/-/default-gateway-4.2.0.tgz#167104c7500c2115f6dd69b0a536bb8ed720552b"