Skip to content
Snippets Groups Projects
Commit 60b66055 authored by Renaud Le Gac's avatar Renaud Le Gac
Browse files

Polish the JsonWriter -- stay as close as possible to the default implementation.

In the ExtJs 3.1 implementation modify values are encapsulated in a dictionary data which is sent to the server as a params. Use it and modified the server side accordingly.
parent e3a2081a
No related branches found
No related tags found
No related merge requests found
...@@ -34,6 +34,11 @@ def index(): ...@@ -34,6 +34,11 @@ def index():
print "Arguments:", request.vars print "Arguments:", request.vars
try: try:
# decode the data dictionary
if "data" in request.vars and isinstance(request.vars.data, str):
request.vars.data = json.loads(request.vars.data)
di = dbSvc.proceed(request.vars) di = dbSvc.proceed(request.vars)
resp = json.dumps(di) resp = json.dumps(di)
......
...@@ -11,7 +11,7 @@ from gluon.http import HTTP ...@@ -11,7 +11,7 @@ from gluon.http import HTTP
ACTION_INVALID = "Requested action '%s' is not implemented." ACTION_INVALID = "Requested action '%s' is not implemented."
FIELD_NOT_IN_DB = "the field '%s' doesn't in the database." FIELD_NOT_IN_DB = "the field '%s' doesn't exist in the database."
RECORD_DELETED = "Record %s is deleted." RECORD_DELETED = "Record %s is deleted."
RECORD_INSERTED = "Record is inserted with id %i." RECORD_INSERTED = "Record is inserted with id %i."
RECORD_UPDATED = "Record %s is updated." RECORD_UPDATED = "Record %s is updated."
...@@ -70,7 +70,8 @@ class DbSvc(BaseSvc): ...@@ -70,7 +70,8 @@ class DbSvc(BaseSvc):
""" """
KEYWORDS = ['debug', KEYWORDS = ['data',
'debug',
'dir', 'dir',
'foreignFields', 'foreignFields',
'id', 'id',
...@@ -85,10 +86,21 @@ class DbSvc(BaseSvc): ...@@ -85,10 +86,21 @@ class DbSvc(BaseSvc):
def _get_fields(self): def _get_fields(self):
"""Return a dictionary containing field and value. """Return a dictionary containing field and value.
In some case the ajax request send the column and their values The ajax request send the column and their values
as steering keywords. This method allow to extract them. as steering keywords for FormPanel.
With the JsonWriter in EXtJs 3.1, the fields values are
encapsulated in the data dictionary.
This method allow to extract them.
""" """
# New approach coming with ExtJs 3.1 where fields values are
# encapsulated in the data dictionary
if 'data' in self._vars:
return self._vars['data']
# preserve backward compatibility for fromPanel
fields = dict(self._vars) fields = dict(self._vars)
for key in self.KEYWORDS: for key in self.KEYWORDS:
if key in fields: if key in fields:
...@@ -148,24 +160,23 @@ class DbSvc(BaseSvc): ...@@ -148,24 +160,23 @@ class DbSvc(BaseSvc):
table table
the table name where record will be removed. the table name where record will be removed.
id data
identifier of the record (primary key) dictionary with the identifier of the record (id)
Return a dictionary with status and error message: Return a dictionary with status and error message:
{success: True, msg: 'blalbla'} {success: True, msg: 'blalbla'}
""" """
self._debug("Start HttpDb.destroy") self._debug("Start DbSvc.destroy")
self._is_keyword("table") self._is_keyword("table")
table = self._vars.table table = self._vars.table
self._is_keyword("id") self._is_keyword("data")
id = self._vars.id id = self._vars.data["id"]
del self._db[table][id] del self._db[table][id]
self._debug("End HttpDb.destroy") self._debug("End DbSvc.destroy")
return {"success": True, "msg": RECORD_DELETED % id} return {"success": True, "msg": RECORD_DELETED % id}
...@@ -184,7 +195,7 @@ class DbSvc(BaseSvc): ...@@ -184,7 +195,7 @@ class DbSvc(BaseSvc):
{success: True, data:{field1: default1,....}} {success: True, data:{field1: default1,....}}
""" """
self._debug("Start HttpDb.get_defaults") self._debug("Start DbSvc.get_defaults")
self._is_keyword("table") self._is_keyword("table")
table = self._vars.table table = self._vars.table
...@@ -194,7 +205,7 @@ class DbSvc(BaseSvc): ...@@ -194,7 +205,7 @@ class DbSvc(BaseSvc):
for field in self._db[table].fields: for field in self._db[table].fields:
di[field] = self._db[table][field].default di[field] = self._db[table][field].default
self._debug("End HttpDb.get_defaults") self._debug("End DbSvc.get_defaults")
return {"success": True, "data": di} return {"success": True, "data": di}
...@@ -214,7 +225,7 @@ class DbSvc(BaseSvc): ...@@ -214,7 +225,7 @@ class DbSvc(BaseSvc):
{success: True, data:[field1, field2,....]} {success: True, data:[field1, field2,....]}
""" """
self._debug("Start HttpDb.get_fields") self._debug("Start DbSvc.get_fields")
self._is_keyword("table") self._is_keyword("table")
table = self._vars.table table = self._vars.table
...@@ -222,7 +233,7 @@ class DbSvc(BaseSvc): ...@@ -222,7 +233,7 @@ class DbSvc(BaseSvc):
li = self._db[table].fields li = self._db[table].fields
self._debug("End HttpDb.get_fields") self._debug("End DbSvc.get_fields")
return {"success": True, "data": li} return {"success": True, "data": li}
...@@ -238,17 +249,15 @@ class DbSvc(BaseSvc): ...@@ -238,17 +249,15 @@ class DbSvc(BaseSvc):
{success: True, data:[table1, table2,....]} {success: True, data:[table1, table2,....]}
""" """
self._debug("Start HttpDb.get_tables") self._debug("Start DbSvc.get_tables")
li = self._db.tables li = self._db.tables
self._debug("End HttpDb.get_tables") self._debug("End DbSvc.get_tables")
return {"success": True, "data": li} return {"success": True, "data": li}
def create(self): def create(self):
"""Create a new record in the database. """Create a new record in the database.
This service is strongly linked to a Ext.FormPanel widget.
The steering keywords are: The steering keywords are:
debug debug
...@@ -257,9 +266,12 @@ class DbSvc(BaseSvc): ...@@ -257,9 +266,12 @@ class DbSvc(BaseSvc):
table table
the table name where data will be inserted. the table name where data will be inserted.
field1, field2,... field1, field2,... (backward compatibility with form)
the field value. the field value.
data
a dictionary with field values
Return a dictionary with status, error message and id of the insert row: Return a dictionary with status, error message and id of the insert row:
{success: False, errors: {field: 'blabla',..}} {success: False, errors: {field: 'blabla',..}}
The keyword success and error are required by The keyword success and error are required by
...@@ -270,7 +282,7 @@ class DbSvc(BaseSvc): ...@@ -270,7 +282,7 @@ class DbSvc(BaseSvc):
{success: True, msg: 'blalbla', data:{id:xx}} {success: True, msg: 'blalbla', data:{id:xx}}
""" """
self._debug("Start HttpDb.insert") self._debug("Start DbSvc.insert")
self._is_keyword("table") self._is_keyword("table")
table = self._vars.table table = self._vars.table
...@@ -285,7 +297,7 @@ class DbSvc(BaseSvc): ...@@ -285,7 +297,7 @@ class DbSvc(BaseSvc):
id = self._db[table].insert (**fields) id = self._db[table].insert (**fields)
self._debug("End HttpDb.insert.") self._debug("End DbSvc.insert.")
return {"success": True, "msg": RECORD_INSERTED % id, "data": {"id": id}} return {"success": True, "msg": RECORD_INSERTED % id, "data": {"id": id}}
...@@ -299,12 +311,12 @@ class DbSvc(BaseSvc): ...@@ -299,12 +311,12 @@ class DbSvc(BaseSvc):
""" """
BaseSvc.proceed(self, vars) BaseSvc.proceed(self, vars)
self._debug("Start HttpDb.proceed") self._debug("Start DbSvc.proceed")
self._is_keyword("xaction") self._is_keyword("xaction")
self._is_action_valid(vars.xaction) self._is_action_valid(vars.xaction)
self._debug("End HttpDb.proceed") self._debug("End DbSvc.proceed")
return eval("self.%s()" % vars.xaction) return eval("self.%s()" % vars.xaction)
...@@ -341,7 +353,7 @@ class DbSvc(BaseSvc): ...@@ -341,7 +353,7 @@ class DbSvc(BaseSvc):
{success: True, data: [{field1: xxx,..},...]} {success: True, data: [{field1: xxx,..},...]}
""" """
self._debug("Start HttpDb.read") self._debug("Start DbSvc.read")
self._is_keyword("table") self._is_keyword("table")
table = self._vars.table table = self._vars.table
...@@ -397,14 +409,13 @@ class DbSvc(BaseSvc): ...@@ -397,14 +409,13 @@ class DbSvc(BaseSvc):
# The simple way is to use JSON encoding and decoding # The simple way is to use JSON encoding and decoding
li = json.loads(rows.json()) li = json.loads(rows.json())
self._debug("End HttpDb.read") self._debug("End DbSvc.read")
return {"success": True, "data": li} return {"success": True, "data": li}
def update(self): def update(self):
"""Update a record in the database. """Update a record in the database.
This service is strongly linked to a Ext.FormPanel widget.
The steering keywords are: The steering keywords are:
debug debug
...@@ -413,17 +424,16 @@ class DbSvc(BaseSvc): ...@@ -413,17 +424,16 @@ class DbSvc(BaseSvc):
table table
the table name where data will be updated the table name where data will be updated
id data
The identifier of the record to be updated (primary key) A dictionary containing the update values for field
as well as the identifier of the record to be updated (id)
field1, field2,...
the field value.
Return a dictionary with status, message and id of the update row: Return a dictionary with status, message and id of the update row:
{success: True, msg: 'blalbla', data:{id:xx}} {success: True, msg: 'blalbla', data:{id:xx}}
""" """
self._debug("Start HttpDb.update.") self._debug("Start DbSvc.update.")
self._is_keyword("table") self._is_keyword("table")
table = self._vars.table table = self._vars.table
...@@ -432,8 +442,8 @@ class DbSvc(BaseSvc): ...@@ -432,8 +442,8 @@ class DbSvc(BaseSvc):
fields = self._get_fields() fields = self._get_fields()
self._is_fields_in_db(table, fields) self._is_fields_in_db(table, fields)
self._is_keyword("id") self._is_keyword("data")
id = self._vars.id id = self._vars.data["id"]
self._db[table][id] = fields self._db[table][id] = fields
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
* In the current implementation, the target table, the request columns * In the current implementation, the target table, the request columns
* as well as the request foreign keys are specified via the model property. * as well as the request foreign keys are specified via the model property.
* *
* Design to work with the class HttpDb on the server side. * Design to work with the class DbSvc on the server side.
* *
* MANDATORY PROPERTIES: * MANDATORY PROPERTIES:
* - model * - model
...@@ -17,11 +17,109 @@ ...@@ -17,11 +17,109 @@
* @author $Author$ * @author $Author$
* @version $Id$ * @version $Id$
* *
* TODO: implement the restful approach
*
*/ */
Ext.namespace('App.data'); Ext.namespace('App.data');
/**
* Helper class to addapt the Ext.JsonWriter to App.data.JsonStore
* where baseParams contains the target table and the field which are red.
*/
App.data.JsonWriter = Ext.extend(Ext.data.JsonWriter, {
/**
* Pre-defined properties
*/
encode: true,
encodeDelete: true,
/**
* constructor
*/
constructor: function(config){
App.data.JsonWriter.superclass.constructor.call(this, config);
},
/**
* The render function finalizes the params object.
* In the base class, this method copies the baseParams and the data.
* Addapt the base class method to remove the baseParams tableFields,
* and foreignFields which are not used by the server for the create,
* and destroy transaction.
*
* @param {Object} params
* @param {Object} baseParams
* @param {Object} data
*/
render: function(params, baseParams, data){
// For a foreign field the modified record contain the fieldValue
// as well as the displayValue. The following line remove the displayValue.
for (var field in data){
for (var i in baseParams.foreignFields){
if (field == baseParams.foreignFields[i][2]){
delete data[field];
}
}
}
// the baseParams defined the table and the list of fields read.
// baseParams are copies in all transactions (create, update and destroy)
// remove the the tableFields and the foreignFields
var tmp = Ext.apply({}, baseParams);
delete baseParams.tableFields;
delete baseParams.foreignFields;
// execute the underlying method
App.data.JsonWriter.superclass.render.apply(this, arguments);
// restore the baseParams
Ext.apply(baseParams, tmp);
}
});
/**
* HTTP Exception handler
* @param {Object} dataproxy
* @param {Object} type
* @param {Object} action
* @param {Object} options
* @param {Object} response
* @param {Object} args
*/
App.data.httpException = function(dataproxy, type, action, options, response, args){
if(response.status == 200){
// handle error coming from validation step on the server
// decode the return json string
var dict = Ext.decode(response.responseText);
// build the error message
var text = new String();
for(item in dict.errors){
text += "Field <i>"+item+"</i>: "+dict.errors[item]+"<br>";
}
text += "The record is not <i>"+action+"</i> in the database."
// popup an alert
Ext.MessageBox.show({
title: "DATABASE ERROR",
msg: text,
buttons: Ext.MessageBox.OK,
icon: Ext.MessageBox.ERROR
});
} else {
// handle all other king of errors
Ext.MessageBox.show({
title: response.statusText,
msg: response.responseText,
buttons: Ext.MessageBox.OK,
icon: Ext.MessageBox.ERROR
});
}
};
/**
* App.data.JsnStore
*/
App.data.JsonStore = Ext.extend(Ext.data.Store, { App.data.JsonStore = Ext.extend(Ext.data.Store, {
/** /**
...@@ -38,21 +136,20 @@ App.data.JsonStore = Ext.extend(Ext.data.Store, { ...@@ -38,21 +136,20 @@ App.data.JsonStore = Ext.extend(Ext.data.Store, {
/** /**
* The constructor * The constructor
* @param {Object} config
*/ */
constructor: function(config){ constructor: function(config){
Ext.apply(this, config); Ext.apply(this, config);
if (!this.model){ if (!this.model) {
throw new Error("the property model is missing !!!") throw new Error("the property model is missing !!!")
} }
// Setup json reader if not defined in config // Setup json reader if not defined in config
// The list of fields includes foreign keys. // The list of fields includes foreign keys.
// the name of the field for a foreign key is equal to foreignField // the name of the field for a foreign key is equal to foreignField
if (!config.reader) { if (!config.reader) {
var userFields = new Array(); var userFields = new Array();
userFields = userFields.concat(this.model.tableFields); userFields = userFields.concat(this.model.tableFields);
...@@ -71,91 +168,29 @@ App.data.JsonStore = Ext.extend(Ext.data.Store, { ...@@ -71,91 +168,29 @@ App.data.JsonStore = Ext.extend(Ext.data.Store, {
// Setup the json writer if not defined in config // Setup the json writer if not defined in config
if (!config.writer) { if (!config.writer) {
this.writer = new Ext.data.JsonWriter({ this.writer = new App.data.JsonWriter();
// When a record is destroy th server required its id
destroyRecord: function(record){
return {"id": record.id};
},
// TODO: modify the server side to understand basic writer - Remove render
render: function(params, baseParams, data){
// For a foreign field the modified record contain
// the fieldValue as well as the displayValue.
// The following line remove the displayValue.
for (var field in data){
for (var i in baseParams.foreignFields){
if (field == baseParams.foreignFields[i][2]){
delete data[field];
}
}
}
// HACK: TO KEEP BACKWARD COMPATIBILITY
// param is the object send via the POST
// Here we concatenate the data and the baseParams
// required by the server to process the transaction.
// The required action add by the JsonWritter via the
// keyword xaction (ExtJs.3.1)
Ext.apply(params, data);
params["debug"] = baseParams["debug"];
params["table"] = baseParams["table"];
}
});
} }
// construct the underlying class. DON'T MOVE // construct the underlying class. DON'T MOVE
App.data.JsonStore.superclass.constructor.apply(this); App.data.JsonStore.superclass.constructor.apply(this);
// Setup the baseParams parameters // Setup the baseParams parameters
// They are always send to the server and allows to steer the answer // They are always send to the server and allows to steer the answer
// This array have to be built after the call to the constructor // They contains the name of the table and the list of fields to be red.
// They can be modified an any time by the user // This array have to be built after the call to the constructor and
// can be modified an any time by the user
this.baseParams = { this.baseParams = {
debug: this.model.debug, debug: this.model.debug,
table: this.model.table, table: this.model.table,
tableFields: this.model.tableFields, tableFields: this.model.tableFields,
} }
// add the foreign key // add the foreign key
if (this.model.foreignFields){ if (this.model.foreignFields) {
this.baseParams.foreignFields = this.model.foreignFields; this.baseParams.foreignFields = this.model.foreignFields;
} }
// add a listener to catch HTTP failure // add a listener to catch HTTP failure
this.addListener('exception', function(dataproxy, type, action, options, response, args){ this.addListener('exception', App.data.httpException);
if(response.status == 200){
// handle error coming from validation step on the server
// decode the return json string
var dict = Ext.decode(response.responseText);
// build the error message
var text = new String();
for(item in dict.errors){
text += "Field <i>"+item+"</i>: "+dict.errors[item]+"<br>";
}
text += "The record is not <i>"+action+"</i> in the database."
// popup an alert
Ext.MessageBox.show({
title: "DATABASE ERROR",
msg: text,
buttons: Ext.MessageBox.OK,
icon: Ext.MessageBox.ERROR
});
} else {
// handle all other king of errors
Ext.MessageBox.show({
title: response.statusText,
msg: response.responseText,
buttons: Ext.MessageBox.OK,
icon: Ext.MessageBox.ERROR
});
}
});
} }
}); });
...@@ -2,9 +2,10 @@ ...@@ -2,9 +2,10 @@
* store_1.js * store_1.js
* *
* To test and debug store. * To test and debug store.
* $Id: store_1.js -1 $ * $Id$
* *
*/ */
Ext.namespace('App.data');
/** /**
...@@ -13,14 +14,14 @@ ...@@ -13,14 +14,14 @@
var main = function(provider, response){ var main = function(provider, response){
var model = response.result var model = response.result.model;
// console.log('model');
// console.dir(model.store);
var store = new App.data.JsonStore({ var store = new App.data.JsonStore({
url: App.dburl, url: App.dburl,
table: model.table, model: model.store,
tableFields: model.store.tableFields,
foreignFields: model.store.foreignFields,
debug: true,
autoSave: false, autoSave: false,
}); });
...@@ -35,28 +36,24 @@ var main = function(provider, response){ ...@@ -35,28 +36,24 @@ var main = function(provider, response){
store.on("load",function(){ store.on("load",function(){
console.log("store loaded: "+this.getCount()+" records") console.log("store loaded: "+this.getCount()+" records")
/*
// update the record // update the record
var record1 = this.getAt(10); var record1 = this.getAt(6);
record1.set("note", "a ab abc"); record1.set("note", "a ab abc");
record1.set("date", "2010-01-31"); record1.set("date", "2010-01-31");
this.save(); // this.save();
*/
/*
// Add a new record // Add a new record
var record2 = new this.recordType(); var record2 = new this.recordType();
record2.set("version", "2011.0"); record2.set("version", "2011.0");
this.add(record2); this.add(record2);
this.insert(0, record2); this.insert(0, record2);
*/ // this.save();
/*
// Delete a record // Delete a record
var record3 = this.getAt(this.find("id", "56")); var record3 = this.getAt(this.find("id", "7"));
this.remove(record3); this.remove(record3);
*/ this.save();
}, store); }, store);
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment