2
0
mirror of https://github.com/frappe/books.git synced 2024-09-20 03:29:00 +00:00

[refactor] extending forms and lists

This commit is contained in:
Rushabh Mehta 2018-01-24 17:22:05 +05:30
parent 1e9701d0be
commit 84d39be287
19 changed files with 159 additions and 146 deletions

View File

@ -23,11 +23,15 @@ class sqliteDatabase {
}
async migrate() {
for (let doctype in frappe.models.data.doctype) {
if (await this.table_exists(doctype)) {
await this.alter_table(doctype);
} else {
await this.create_table(doctype);
for (let doctype in frappe.modules) {
// check if controller module
if (frappe.modules[doctype].Meta) {
if (await this.table_exists(doctype)) {
await this.alter_table(doctype);
} else {
await this.create_table(doctype);
}
}
}
await this.commit();

View File

@ -2,8 +2,8 @@ const frappe = require('frappejs');
const Search = require('./search');
const Router = require('frappejs/common/router');
const Page = require('frappejs/client/view/page');
const List = require('frappejs/client/view/list');
const Form = require('frappejs/client/view/form');
const BaseList = require('frappejs/client/view/list');
const BaseForm = require('frappejs/client/view/form');
const Navbar = require('./navbar');
module.exports = class Desk {
@ -58,8 +58,8 @@ module.exports = class Desk {
get_list_page(doctype) {
if (!this.pages.lists[doctype]) {
let page = new Page('List ' + doctype);
page.list = new List({
let page = new Page('List ' + frappe.get_meta(doctype).name);
page.list = new (this.get_view_class(doctype, 'List', BaseList))({
doctype: doctype,
parent: page.body
});
@ -73,8 +73,8 @@ module.exports = class Desk {
get_form_page(doctype) {
if (!this.pages.forms[doctype]) {
let page = new Page('Edit ' + doctype);
page.form = new Form({
let page = new Page('Edit ' + frappe.get_meta(doctype).name);
page.form = new (this.get_view_class(doctype, 'Form', BaseForm))({
doctype: doctype,
parent: page.body
});
@ -91,6 +91,20 @@ module.exports = class Desk {
return this.pages.forms[doctype];
}
get_view_class(doctype, class_name, default_class) {
let client_module = this.get_client_module(doctype);
if (client_module && client_module[class_name]) {
return client_module[class_name];
} else {
return default_class;
}
}
get_client_module(doctype) {
return frappe.modules[`${doctype}_client`];
}
add_sidebar_item(label, action) {
let item = frappe.ui.add('a', '', frappe.ui.add('p', null, frappe.desk.sidebar));
item.textContent = label;

View File

@ -97,6 +97,14 @@ class BaseControl {
value = await this.validate(value);
await this.doc.set(this.fieldname, value);
}
disable() {
this.input.setAttribute('disabled', 'disabled');
}
enable() {
this.input.removeAttribute('disabled');
}
}
module.exports = BaseControl;

View File

@ -1,7 +1,7 @@
const frappe = require('frappejs');
const controls = require('./controls');
module.exports = class Form {
module.exports = class BaseForm {
constructor({doctype, parent, submit_label='Submit'}) {
this.parent = parent;
this.doctype = doctype;
@ -11,6 +11,9 @@ module.exports = class Form {
this.controls_list = [];
this.meta = frappe.get_meta(this.doctype);
if (this.setup) {
this.setup();
}
this.make();
}

View File

@ -1,6 +1,6 @@
const frappe = require('frappejs');
module.exports = class List {
module.exports = class BaseList {
constructor({doctype, parent, fields}) {
this.doctype = doctype;
this.parent = parent;
@ -18,13 +18,8 @@ module.exports = class List {
async run() {
this.make_body();
this.set_filters();
let data = await this.meta.get_list({
filters: this.filters,
start:this.start,
limit:this.page_length + 1
});
let data = await this.get_data();
for (let i=0; i< Math.min(this.page_length, data.length); i++) {
this.render_row(this.start + i, data[i]);
@ -40,16 +35,31 @@ module.exports = class List {
this.update_more(data.length > this.page_length);
}
async get_data() {
return await frappe.db.get_all({
doctype: this.doctype,
fields: this.get_fields(),
filters: this.get_filters(),
start: this.start,
limit: this.page_length + 1
});
}
get_fields() {
return ['name'];
}
async append() {
this.start += this.page_length;
await this.run();
}
set_filters() {
this.filters = {};
get_filters() {
let filters = {};
if (this.search_input.value) {
this.filters.keywords = ['like', '%' + this.search_input.value + '%'];
filters.keywords = ['like', '%' + this.search_input.value + '%'];
}
return filters;
}
make_body() {
@ -104,10 +114,14 @@ module.exports = class List {
render_row(i, data) {
let row = this.get_row(i);
row.innerHTML = this.meta.get_row_html(data);
row.innerHTML = this.get_row_html(data);
row.style.display = 'block';
}
get_row_html(data) {
return `<a href="#edit/${this.doctype}/${data.name}>${data.name}</a>`;
}
get_row(i) {
if (!this.rows[i]) {
this.rows[i] = frappe.ui.add('div', 'list-row py-2', this.body);

View File

@ -1,8 +1,7 @@
const utils = require('../utils');
const models = require('../model/models');
const model = require('../model');
const _document = require('../model/document');
const meta = require('../model/meta');
const BaseDocument = require('../model/document');
const BaseMeta = require('../model/meta');
const _session = require('../session');
const errors = require('./errors');
@ -11,9 +10,8 @@ module.exports = {
init_libs(frappe) {
Object.assign(frappe, utils);
frappe.model = model;
frappe.models = new models.Models();
frappe.document = _document;
frappe.meta = meta;
frappe.BaseDocument = BaseDocument;
frappe.BaseMeta = BaseMeta;
frappe._session = _session;
frappe.errors = errors;
}

View File

@ -15,6 +15,7 @@ module.exports = {
init_globals() {
this.meta_cache = {};
this.modules = {};
this.docs = {};
this.flags = {
cache_docs: false
@ -41,34 +42,46 @@ module.exports = {
get_meta(doctype) {
if (!this.meta_cache[doctype]) {
this.meta_cache[doctype] = new (this.models.get_meta_class(doctype))(this.models.get('DocType', doctype));
this.meta_cache[doctype] = new (this.get_meta_class(doctype))();
}
return this.meta_cache[doctype];
},
init_controller(doctype, module) {
get_meta_class(doctype) {
doctype = this.slug(doctype);
this.models.controllers[doctype] = module[doctype];
this.models.meta_classes[doctype] = module[doctype + '_meta'];
if (this.modules[doctype] && this.modules[doctype].Meta) {
return this.modules[doctype].Meta;
} else {
return this.BaseMeta;
}
},
async get_doc(data, name) {
if (typeof data==='string' && typeof name==='string') {
let doc = this.get_doc_from_cache(data, name);
if (!doc) {
let controller_class = this.models.get_controller(data);
let controller_class = this.get_controller_class(data);
doc = new controller_class({doctype:data, name: name});
await doc.load();
this.add_to_cache(doc);
}
return doc;
} else {
let controller_class = this.models.get_controller(data.doctype);
let controller_class = this.get_controller_class(data.doctype);
var doc = new controller_class(data);
}
return doc;
},
get_controller_class(doctype) {
doctype = this.slug(doctype);
if (this.modules[doctype] && this.modules[doctype].Document) {
return this.modules[doctype].Document;
} else {
return this.BaseDocument;
}
},
async get_new_doc(doctype) {
let doc = await frappe.get_doc({doctype: doctype});
doc.set_name();

View File

@ -1,6 +1,6 @@
const frappe = require('frappejs');
class Document {
module.exports = class BaseDocument {
constructor(data) {
this.handlers = {};
this.setup();
@ -143,6 +143,4 @@ class Document {
await this.trigger('after_update', 'after_save');
return this;
}
};
module.exports = { Document: Document };
};

View File

@ -1,7 +1,7 @@
const Document = require('./document').Document;
const BaseDocument = require('./document');
const frappe = require('frappejs');
class Meta extends Document {
module.exports = class BaseMeta extends BaseDocument {
constructor(data) {
super(data);
this.event_handlers = {};
@ -100,22 +100,4 @@ class Meta extends Document {
}
}
}
// collections
async get_list({start, limit=20, filters}) {
return await frappe.db.get_all({
doctype: this.name,
fields: this.list_options.fields,
filters: filters,
start: start,
limit: limit
});
}
get_row_html(data) {
return `<a href="/view/${this.name}/${data.name}">${data.name}</a>`;
}
}
module.exports = { Meta: Meta }
}

View File

@ -1,25 +0,0 @@
const process = require('process');
const frappe = require('frappejs');
class Models {
constructor() {
this.data = {doctype: {}};
this.controllers = {};
this.meta_classes = {};
}
get(doctype, name) {
return this.data[frappe.slug(doctype)][frappe.slug(name)];
}
get_controller(doctype) {
return this.controllers[frappe.slug(doctype)];
}
get_meta_class(doctype) {
return this.meta_classes[frappe.slug(doctype)] || frappe.meta.Meta;
}
}
module.exports = { Models: Models }

View File

@ -1,19 +1,13 @@
const frappe = require('frappejs');
const BaseMeta = require('frappejs/model/meta');
const BaseDocument = require('frappejs/model/document');
class todo_meta extends frappe.meta.Meta {
class ToDoMeta extends BaseMeta {
setup_meta() {
Object.assign(this, require('./todo.json'));
this.list_options.fields = ['name', 'subject', 'status'];
}
get_row_html(data) {
const sign = data.status === 'Open' ? '' : '✔';
return `<a href="#edit/todo/${data.name}">${sign} ${data.subject}</a>`;
}
}
class todo extends frappe.document.Document {
class ToDo extends BaseDocument {
setup() {
this.add_handler('validate');
}
@ -24,4 +18,7 @@ class todo extends frappe.document.Document {
}
}
module.exports = {todo:todo, todo_meta:todo_meta};
module.exports = {
Document: ToDo,
Meta: ToDoMeta
};

View File

@ -0,0 +1,16 @@
const BaseList = require('frappejs/client/view/list');
const BaseForm = require('frappejs/client/view/form');
class ToDoList extends BaseList {
get_fields() {
return ['name', 'subject', 'status'];
}
get_row_html(data) {
return `<a href="#edit/todo/${data.name}">${data.subject}</a>`;
}
}
module.exports = {
Form: BaseForm,
List: ToDoList
}

View File

@ -5,7 +5,7 @@ const express = require('express');
const app = express();
const frappe = require('frappejs');
const rest_api = require('./rest_api')
const models = require('frappejs/server/models');
const init_models = require('frappejs/server/init_models');
const common = require('frappejs/common');
const bodyParser = require('body-parser');
const path = require('path');
@ -13,11 +13,11 @@ const path = require('path');
module.exports = {
async start({backend, connection_params, static, models_path}) {
await this.init();
models.init_models(path.resolve('node_modules', 'frappejs', 'models'));
models.init_models(models_path);
await this.init_db({backend:backend, connection_params:connection_params});
this.init_models(models_path);
// database
await this.init_db({backend:backend, connection_params:connection_params});
// app
app.use(bodyParser.json());
@ -30,7 +30,14 @@ module.exports = {
// listen
frappe.app = app;
frappe.server = app.listen(frappe.config.port);
},
init_models(models_path) {
// import frappe modules
init_models(path.join(path.dirname(require.resolve('frappejs')), 'models'));
// import modules from the app
init_models(models_path);
},
async init() {

23
server/init_models.js Normal file
View File

@ -0,0 +1,23 @@
const frappe = require('frappejs');
const walk = require('walk');
const path = require('path');
module.exports = function(models_path) {
if (!models_path) {
return;
}
walk.walkSync(models_path, {
listeners: {
file: (basepath, file_data, next) => {
const doctype = path.basename(path.dirname(basepath));
const name = path.basename(basepath);
const file_path = path.resolve(basepath, file_data.name);
if (doctype==='doctype' && file_data.name.endsWith('.js')) {
frappe.modules[file_data.name.slice(0, -3)] = require(file_path);
}
next();
}
}
});
}

View File

@ -1,42 +0,0 @@
const frappe = require('frappejs');
const walk = require('walk');
const path = require('path');
module.exports = {
init_models(models_path) {
const setup_model = (doctype, name, file_path) => {
// add to frappe.models.data
if (!frappe.models[doctype]) {
frappe.models[doctype] = {};
}
frappe.models.data[doctype][name] = require(file_path);
}
const setup_controller = (doctype, file_path) => {
let _module = require(file_path);
frappe.models.controllers[doctype] = _module[doctype];
if (_module[doctype + '_meta']) {
frappe.models.meta_classes[doctype] = _module[doctype + '_meta'];
}
}
walk.walkSync(models_path, {
listeners: {
file: (basepath, file_data, next) => {
const doctype = path.basename(path.dirname(basepath));
const name = path.basename(basepath);
const file_path = path.resolve(basepath, file_data.name);
if (file_data.name.endsWith('.json')) {
setup_model(doctype, name, file_path)
}
if (doctype==='doctype' && file_data.name.endsWith('.js')) {
setup_controller(path.basename(basepath), file_path);
}
next();
}
}
});
}
}

View File

@ -4,7 +4,6 @@ module.exports = {
setup(app) {
// get list
app.get('/api/resource/:doctype', frappe.async_handler(async function(request, response) {
let fields, filters;
for (key of ['fields', 'filters']) {
if (request.query[key]) {
request.query[key] = JSON.parse(request.query[key]);

View File

@ -2,7 +2,8 @@ const server = require('frappejs/server');
module.exports = {
async init_sqlite() {
server.init()
server.init();
server.init_models();
server.init_db({
backend: 'sqlite',
connection_params: {db_path: 'test.db'}

View File

@ -8,7 +8,7 @@ describe('Models', () => {
});
it('should get todo json', () => {
let todo = frappe.models.get('DocType', 'ToDo');
let todo = frappe.get_meta('todo');
assert.equal(todo.issingle, 0);
});
});

View File

@ -1,5 +1,8 @@
const server = require('frappejs/server');
if (require.main === module) {
server.start({backend: 'sqlite', connection_params: {db_path: 'test.db'}});
server.start({
backend: 'sqlite',
connection_params: {db_path: 'test.db'}
});
}