wiki:DeveloperGuidelines/Tutorial/RESTCustomisation

Version 1 (modified by Fran Boon, 14 years ago) ( diff )

--

REST Customisation

It seems generally difficult for new developers to understand how to take control of functionality in the RESTController of the S3 framework.

IMHO that is because most of the examples look like that:

def myresource():
   """ RESTful CRUD controller """
   return shn_rest_controller(module, resource)

This in fact leaves it all to the shn_rest_controller function - hard to see what really happens.

However, shn_rest_controller does not actually mean you would be tied to the CRUD method handlers of 01_crud.py either. REST merely cares for the proper resolution of URLs and HTTP methods, and then calls hooked-in functions to process the requests.

First of all - shn_rest_controller returns some output:

def myresource():
   """ RESTful CRUD controller """
   output = shn_rest_controller(module, resource)
   return output

In case of interactive views, this output would be a dict with the components that are then passed to the view (by "return output" here). This could contain a form, for example:

def myresource():
   """ RESTful CRUD controller """
   output = shn_rest_controller(module, resource)
   if isinstance(output, dict) and "form" in output:
       form = output.get("form")
       <your form manipulation here>
   return output

That also means, you can add more items to the output dict:

def myresource():
   """ RESTful CRUD controller """
   output = shn_rest_controller(module, resource)
   if isinstance(output, dict):
       if "form" in output:
           form = output.get("form")
           <your form manipulation here>
       myitem = "I can send this item to the view"
       output.update(myitem=myitem)
   return output

Secondly, the shn_rest_controller has multiple hooks, among others for CRUD method handlers. Look at how shn_rest_controller is actually defined:

def shn_rest_controller(module, resource, **attr):

   s3rest.set_handler("import_xml", import_xml)
   s3rest.set_handler("import_json", import_json)
   s3rest.set_handler("list", shn_list)
   s3rest.set_handler("read", shn_read)
   s3rest.set_handler("create", shn_create)
   s3rest.set_handler("update", shn_update)
   s3rest.set_handler("delete", shn_delete)
   s3rest.set_handler("search", shn_search)
   s3rest.set_handler("options", shn_options)

   output = s3rest(session, request, response, module, resource, **attr)
   return output

This is nothing else than a wrapper for the global s3rest object (an instance of S3RESTController), which configures the CRUD handlers of 01_crud.py as handlers for CRUD methods. You don't have to use shn_rest_controller to have a RESTful API, you can make your own handler configuration and call s3rest directly.

But even when using shn_rest_controller, you can take control over what actually happens. A very comfortable (and recommended) way to get control over s3rest when using shn_rest_controller is to hook in prep and postp functions. Look at S3REST to find out when prep and postp hooks are invoked.

A prep hook would allow you to change a handler configuration in certain situations, e.g. testing a URL variable:

def myresource():
   """ RESTful CRUD controller """

   def _prep(jr):
       mylist = jr.request.vars.get("mylist")
       if mylist:
           s3rest.set_handler("list", my_list_controller)
       return True # do not forget to return True!
   response.s3.prep = _prep

   output = shn_rest_controller(module, resource)
   return output

This example would switch to my_list_controller instead of shn_list in case there is a ?mylist= in the URL. In all other cases, the default handlers are executed as usual, you still have a RESTful API for your resources.

While you can define prep's and postp's as local functions (as in the example above) or even lambdas, it is also very well possible to create more generic, reusable prep and postp functions (e.g. to implement different method handler configurations, or to catch certain situations to bypass the CRUD handlers, or to manipulate the output dict in a certain way).

A very important structure during prep and postp is the S3RESTRequest object (usually instantiated as "jr"). This object contains all necessary information to process the current REST request, and it is passed to both prep and postp. See S3REST for a list of attributes and methods.

However - there are plenty of possibilities.

Dominic

Note: See TracWiki for help on using the wiki.