I have been working on a single page web application called Moolah. Moolah uses a restful style to get data from the server in json format. When I started working on this, I couldn’t find a good pluggable module that would allow me to do this easily, so I wrote my own.
This module only works for json currently, but wouldn’t be very much work to extend to xml or other formats.
When POSTing a record, the result is the new record (including the new key). In the case of a validation failure, the errors object is returned instead.
I did some (non-extensive) searching for what a restful url scheme should look like, and didn’t really find anything conclusive. The url patterns that it uses are as follows:
get /data/account/id/123 (get item with id 123
get /data/account/search/123 (get items that contain 123
get /data/account/list/0/10?field=val (get a list from item 0 to item 10, where field=val
post /data/account/ (create a record)
delete /data/account/123 (delete a record with id 123)
It is possible that I should move to a url-pattern that looks more like:
get /data/account/123 (get item with id 123
get /data/account/?query=123 (get items that contain 123 (text search)
get /data/account/?start=0&items=10 (get a list from item 0 to item 10
post /data/account/ (create a record)
delete /data/account/123 (delete a record with id 123)
I didn’t find this urlpattern as natural, but am willing to have my mind changed.
It is written to be compatible with Google App Engine using app-engine-patch and uses the guidelines at app-engine-patch for making reusable modules.
It is completely pluggable and requires very little configuration. To use you must do the following:
- Within the main project:
- extract the zip file as a new application within your project
- modify the root-level settings.py file to add the “restful” application
- Within the application:
- Create a UserRestfulController class that inherits from restful.RestfulController
class AccountForm(ModelForm):
class Meta:
model = Account
# exclude any fields that shouldn’t be set or overridden from a POST
exclude=(‘created’,'owner’)
class AccountRestfulController(RestfulController):
modelClass = Account
formClass = AccountForm
- modify the application settings.py file and add “REST_CONTROLLERS={“account”:AccountRestfulController}” (where you want to expose a “User” model object.
- Then (as an example using jQuery) you can access the data with something like:
getAccount: function(key, callback) {
$.get("/data/account/id/" + key, function(data) {
callback(new CORE.Account(JSON.parse(data, CORE.util.stringToDate)[0]));
});
},
getAccounts : function(offset, limit, callback) {
$.get("/data/account/list/" + offset + "/" + limit + "/", function(data) {
callback(CORE.convertJsonListToObject(JSON.parse(data, CORE.util.stringToDate),
CORE.Account));
});
},
deleteAccount : function(accountPk, callback) {
$.ajax({
type : "DELETE",
url : "/data/account/" + accountPk,
success : function() {
callback();
}
});
},
Thats it.
There is a fair bit of flexibility in the way that the controller works by overriding methods in the RestController class. eg: We want to return a list of transactions associated with an account:
"/data/futureTransaction/list/" + offset + "/" + limit + "/?accountPk=" + account.pk;
then we have a RestfulController defined:
class TransactionRestfulController(RestfulController):
modelClass = Transaction
formClass = TransactionForm
def list (self, offset, limit):
accountPk = self.request.GET['accountPk']
account = Account.get(accountPk)
return Paginator(account.futuretransaction_set.order("created"), int(limit)).page((int(offset) / int(limit)) + 1).object_list
This gets the requested account, and gets the list of children objects from there and paginates them using standard Django classes.
Files for this post
- restful gae module (.tar.gz, extract to the root of your project and follow the instructions above)