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

Extend basedatabase class in mysql backend

This commit is contained in:
Shridhar 2018-02-08 16:08:53 +05:30
parent 6bd30b1158
commit 2e51a952db

View File

@ -1,10 +1,12 @@
const frappe = require('frappejs'); const frappe = require('frappejs');
const mysql = require('mysql'); const mysql = require('mysql');
const Database = require('./database');
const debug = false; const debug = false;
class mysqlDatabse{ module.exports = class mysqlDatabse extends Database{
constructor({ db_name, username, password, host }) { constructor({ db_name, username, password, host }) {
super();
this.db_name = db_name; this.db_name = db_name;
this.username = username; this.username = username;
this.password = password; this.password = password;
@ -33,78 +35,39 @@ class mysqlDatabse{
} }
async migrate() { async tableExists(table) {
for (let doctype in frappe.modules) {
// check if controller module const name = await this.sql(`SELECT table_name
if (frappe.modules[doctype].Meta) { FROM information_schema.tables
if (await this.table_exists(doctype)) { WHERE table_schema = '${this.db_name}'
await this.alter_table(doctype); AND table_name = '${table}'`);
} else { return (name && name.length) ? true : false;
await this.create_table(doctype);
}
}
}
await this.commit();
}
async create_table(doctype) {
let meta = frappe.get_meta(doctype);
let columns = [];
let values = [];
for (let df of meta.get_valid_fields({ with_children: false })) {
if (this.type_map[df.fieldtype]) {
columns.push(this.get_column_definition(df));
}
} }
async runCreateTableQuery(doctype, columns, values){
const query = `CREATE TABLE IF NOT EXISTS ${frappe.slug(doctype)} ( const query = `CREATE TABLE IF NOT EXISTS ${frappe.slug(doctype)} (
${columns.join(", ")})`; ${columns.join(", ")})`;
return await this.run(query, values); return await this.run(query, values);
} }
close() {
this.conn.close(); getColumnDefinition(df) {
}
get_column_definition(df) {
return `${df.fieldname} ${this.type_map[df.fieldtype]} ${df.reqd && !df.default ? "not null" : ""} ${df.default ? `default '${df.default}'` : ""}` return `${df.fieldname} ${this.type_map[df.fieldtype]} ${df.reqd && !df.default ? "not null" : ""} ${df.default ? `default '${df.default}'` : ""}`
} }
async alter_table(doctype) { async getTableColumns(doctype) {
// get columns return (await this.sql(`SHOW COLUMNS FROM ${doctype}`)).map(d => d.Field);
let table_columns = (await this.sql(`SHOW COLUMNS FROM ${doctype}`)).map(d => d.Field);
let meta = frappe.get_meta(doctype);
let values = [];
for (let df of meta.get_valid_fields({ with_children: false })) {
if (!table_columns.includes(df.fieldname) && this.type_map[df.fieldtype]) {
values = []
if (df.default) {
values.push(df.default);
} }
async runAlterTableQuery(doctype) {
await this.run(`ALTER TABLE ${frappe.slug(doctype)} ADD COLUMN ${this.get_column_definition(df)}`, values); await this.run(`ALTER TABLE ${frappe.slug(doctype)} ADD COLUMN ${this.get_column_definition(df)}`, values);
} }
}
}
async get(doctype, name, fields = '*') { getOne(doctype, name, fields = '*') {
// load parent
let doc = await this.get_one(doctype, name, fields);
// load children fields = this.prepareFields(fields);
let table_fields = frappe.get_meta(doctype).get_table_fields();
for (let field of table_fields) {
doc[fieldname] = await this.get_all({ doctype: field.childtype, fields: ["*"], filters: { parent: doc.name } });
}
return doc;
}
get_one(doctype, name, fields = '*') {
if (fields instanceof Array) {
fields = fields.join(", ");
}
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
this.conn.get(`select ${fields} from ${frappe.slug(doctype)} this.conn.get(`select ${fields} from ${frappe.slug(doctype)}
@ -115,71 +78,23 @@ class mysqlDatabse{
}); });
} }
async insert(doctype, doc) { async insertOne(doctype, doc) {
// insert parent
await this.insert_one(doctype, doc);
// insert children
let table_fields = frappe.get_meta(doctype).get_table_fields();
for (let field of table_fields) {
let idx = 0;
for (let child of (doc[field.fieldname] || [])) {
this.prepare_child(doctype, doc.name, child, field, idx);
await this.insert_one(field.childtype, child);
idx++;
}
}
return doc;
}
async insert_one(doctype, doc) {
let fields = this.get_keys(doctype); let fields = this.get_keys(doctype);
let placeholders = fields.map(d => '?').join(', '); let placeholders = fields.map(d => '?').join(', ');
if (!doc.name) { if (!doc.name) {
doc.name = frappe.get_random_name(); doc.name = frappe.getRandomName();
} }
return await this.run(`insert into ${frappe.slug(doctype)} return await this.run(`insert into ${frappe.slug(doctype)}
(${fields.map(field => field.fieldname).join(", ")}) (${fields.map(field => field.fieldname).join(", ")})
values (${placeholders})`, this.get_formatted_values(fields, doc)); values (${placeholders})`, this.getFormattedValues(fields, doc));
} }
async update(doctype, doc) { async updateOne(doctype, doc) {
// update parent let fields = this.getKeys(doctype);
await this.update_one(doctype, doc);
// insert or update children
let table_fields = frappe.get_meta(doctype).get_table_fields();
for (let field of table_fields) {
// first key is "parent" - for SQL params
let added_children = [doc.name];
for (let child of (doc[field.fieldname] || [])) {
this.prepare_child(doctype, doc.name, child, field, added_children.length - 1);
if (await this.exists(field.childtype, child.name)) {
await this.update_one(field.childtype, child);
} else {
await this.insert_one(field.childtype, child);
}
added_children.push(child.name);
}
// delete other children
// `delete from doctype where parent = ? and name not in (?, ?, ?)}`
await this.run(`delete from ${frappe.slug(field.childtype)}
where
parent = ? and
name not in (${added_children.map(d => '?').join(', ')})`, added_children);
}
return doc;
}
async update_one(doctype, doc) {
let fields = this.get_keys(doctype);
let assigns = fields.map(field => `${field.fieldname} = ?`); let assigns = fields.map(field => `${field.fieldname} = ?`);
let values = this.get_formatted_values(fields, doc); let values = this.getFormattedValues(fields, doc);
// additional name for where clause // additional name for where clause
values.push(doc.name); values.push(doc.name);
@ -188,64 +103,28 @@ class mysqlDatabse{
set ${assigns.join(", ")} where name=?`, values); set ${assigns.join(", ")} where name=?`, values);
} }
prepare_child(parenttype, parent, child, field, idx) { async runDeleteOtherChildren(field, added) {
child.parent = parent; await this.run(`delete from ${frappe.slug(field.childtype)}
child.parenttype = parenttype; where
child.parentfield = field.fieldname; parent = ? and
child.idx = idx; name not in (${added.slice(1).map(d => '?').join(', ')})`, added);
} }
get_keys(doctype) { async deleteOne(doctype, name) {
return frappe.get_meta(doctype).get_valid_fields({ with_children: false }); return await this.run(`delete from ${frappe.slug(doctype)} where name=?`, name);
} }
get_formatted_values(fields, doc) { async deleteChildren(parenttype, parent) {
let values = fields.map(field => { await this.run(`delete from ${parent} where parent=?`, parent);
let value = doc[field.fieldname];
if (value instanceof Date) {
return value.toISOString();
} else {
return value;
}
});
return values;
} }
async delete(doctype, name) {
await this.run(`delete from ${frappe.slug(doctype)} where name=?`, name);
// delete children getAll({ doctype, fields, filters, start, limit, order_by = 'modified', order = 'desc' } = {}) {
let table_fields = frappe.get_meta(doctype).get_table_fields();
for (let field of table_fields) {
await this.run(`delete from ${frappe.slug(field.childtype)} where parent=?`, name)
}
}
async exists(doctype, name) {
return (await this.get_value(doctype, name)) ? true : false;
}
async get_value(doctype, filters, fieldname = 'name') {
if (typeof filters === 'string') {
filters = { name: filters };
}
let row = await this.get_all({
doctype: doctype,
fields: [fieldname],
filters: filters,
start: 0,
limit: 1
});
return row.length ? row[0][fieldname] : null;
}
get_all({ doctype, fields, filters, start, limit, order_by = 'modified', order = 'desc' } = {}) {
if (!fields) { if (!fields) {
fields = frappe.get_meta(doctype).get_keyword_fields(); fields = frappe.getMeta(doctype).getKeywordFields();
} }
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
let conditions = this.get_filter_conditions(filters); let conditions = this.getFilterConditions(filters);
this.conn.all(`select ${fields.join(", ")} this.conn.all(`select ${fields.join(", ")}
from ${frappe.slug(doctype)} from ${frappe.slug(doctype)}
@ -262,32 +141,6 @@ class mysqlDatabse{
}); });
} }
get_filter_conditions(filters) {
// {"status": "Open"} => `status = "Open"`
// {"status": "Open", "name": ["like", "apple%"]}
// => `status="Open" and name like "apple%"
let conditions = [];
let values = [];
for (let key in filters) {
const value = filters[key];
if (value instanceof Array) {
// if its like, we should add the wildcard "%" if the user has not added
if (value[0].toLowerCase() === 'like' && !value[1].includes('%')) {
value[1] = `%${value[1]}%`;
}
conditions.push(`${key} ${value[0]} ?`);
values.push(value[1]);
} else {
conditions.push(`${key} = ?`);
values.push(value);
}
}
return {
conditions: conditions.length ? conditions.join(" and ") : "",
values: values
};
}
run(query, params) { run(query, params) {
// TODO promisify // TODO promisify
return new Promise((resolve, reject) => { return new Promise((resolve, reject) => {
@ -322,14 +175,7 @@ class mysqlDatabse{
} }
} }
async table_exists(table) {
const name = await this.sql(`SELECT table_name
FROM information_schema.tables
WHERE table_schema = '${this.db_name}'
AND table_name = '${table}'`);
return (name && name.length) ? true : false;
}
init_type_map() { init_type_map() {
this.type_map = { this.type_map = {
'Currency': 'real' 'Currency': 'real'
@ -360,6 +206,3 @@ class mysqlDatabse{
} }
} }
} }
module.exports = { Database: mysqlDatabse };