Changes between Version 1 and Version 2 of DeveloperGuidelines/Tutorial/RESTCustomisation


Ignore:
Timestamp:
06/11/10 20:58:57 (15 years ago)
Author:
Dominic König
Comment:

--

Legend:

Unmodified
Added
Removed
Modified
  • DeveloperGuidelines/Tutorial/RESTCustomisation

    v1 v2  
    1 == REST Customisation ==
     1== REST Customisation Mini Tutorial ==
    22
    33It seems generally difficult for new developers to understand how to take control of functionality in the [wiki:RESTController] of the S3 framework.
     
    9090information to process the current REST request, and it is passed to both prep and postp. See [wiki:S3REST#S3RESTRequest] for a list of attributes and methods.
    9191
    92 However - there are plenty of possibilities.
    93 
    94 Dominic
     92Let me give you another recipe:
     93
     94Imagine you have a resource "warehouse" and want to implement a function "report" that generates a report about the current status of the warehouse.
     95
     96Now you're gonna provide this report in a RESTful way, i.e. you want to provide it as a resource that can be addressed via URLs like:
     97
     98{{{
     99http://site.myserver.org/eden/wm/warehouse/report
     100}}}
     101
     102and is provided in several different data formats beyond HTML (the interactive view), let's say - XLS and XML:
     103
     104{{{
     105http://site.myserver.org/eden/wm/warehouse/report.xls
     106http://site.myserver.org/eden/wm/warehouse/report.xml
     107}}}
     108
     109How to tackle this?
     110
     111Yes - you can use shn_rest_controller for this!
     112
     113This could be your "warehouse" controller:
     114
     115{{{
     116def warehouse():
     117   """ RESTful CRUD controller """
     118   return shn_rest_controller(module, resource, ...)
     119}}}
     120
     121At first you implement your report function (in the controller file). This function takes the argument jr (=S3RESTRequest) and a dict of named arguments (just the same named arguments from the shn_rest_controller call above). This function returns the report. Then, in your controller, you plug in this function to your resource - together it would look like that:
     122
     123{{{
     124def warehouse():
     125    """ RESTful CRUD controller """
     126    s3xrc.model.set_method(module, resource,
     127                     method="report", action=warehouse_report)
     128    return shn_rest_controller(module, resource, ...)
     129
     130def warehouse_report(jr, **attr):
     131    """" Warehouse report """
     132    <Code to produce the report>
     133    return report
     134}}}
     135
     136How to implement the report function now? Well - that's entirely up to you. In case if interactive views, you would usually return a dict of values that
     137are then formatted as HTML in the view template:
     138
     139{{{
     140def warehouse_report(jr, **attr):
     141    """" Warehouse report """
     142    <Code to produce the report>
     143    # Assemble the report as dict:
     144    report = dict(title="Page Title", ...)
     145    return report
     146}}}
     147
     148Note that if "report" is a dict, then the REST controller automatically adds the S3RESTRequest as "jr" to that dict before returning it. Thus, "jr" is
     149available to the view templates. That also means: do not use "jr" as key in that dict.
     150
     151However, there may be other formats than HTML - to implement other formats, you might need to check for the representation of the request. This can be
     152found in jr:
     153
     154{{{
     155def warehouse_report(jr, **attr):
     156    """" Warehouse report """
     157    if jr.representation in ("html", "popup"):
     158        <Code to produce the interactive report>
     159        # Assemble the report as dict:
     160        report = dict(title="Page Title", ...)
     161    elif jr.representation == "xls":
     162        <Code to produce the XLS report>
     163    elif jr.representation in shn_xml_export_formats:
     164        <Code to produce the XML report>
     165    else:
     166        session.error = BADFORMAT
     167        redirect(URL(r=jr.request))
     168    return report
     169}}}
     170
     171See [wiki:S3REST#S3RESTRequest S3RESTRequest] to find out more about "jr".
     172
     173To produce the XML report, it is probably sufficient to just export the requested warehouse information in S3XRC-XML, and then use XSLT stylesheets to produce the finally desired XML formats. That's pretty easy:
     174
     175{{{
     176def warehouse_report(jr, **attr):
     177    ...
     178    elif jr.representation in shn_xml_export_formats:
     179        return export_xml(xrequest)
     180    ...
     181}}}
     182
     183Perhaps you want to add an RSS feed:
     184
     185{{{
     186def warehouse_report(jr, **attr):
     187    ...
     188    elif jr.representation == "rss":
     189        <Code to produce the RSS report>
     190        return report
     191    ...
     192}}}
     193
     194Ah - now I forgot to mention how you can get at the data in the warehouse_report example. Your implementation already supports a variety of URLs:
     195
     196This URL addresses the report for all warehouses:
     197{{{
     198http://site.myserver.org/eden/wm/warehouse/report
     199}}}
     200
     201This URL addresses the report for the warehouse record with ID=1.
     202{{{
     203http://site.myserver.org/eden/wm/warehouse/1/report
     204}}}
     205
     206This URL addresses the report in XLS format for the warehouse record with the UUID=123654278 (assuming that you have UUID's in your warehouse table).
     207{{{
     208http://site.myserver.org/eden/wm/warehouse/report.xls?warehouse.uid=123654278
     209}}}
     210
     211The S3RESTRequest provides the resource information to your warehouse_report function.
     212
     213In case a specific record has been requested, you can access it as:
     214{{{
     215    record = jr.record
     216}}}
     217If jr.record is None, then the request is targeting all warehouse records, so you'd take:
     218{{{
     219    table = jr.table
     220    records = db().select(table.ALL)
     221    for record in records:
     222       ...
     223}}}
     224instead.
     225
     226NOTE: representation in jr is always all lowercase, there is no differentiation between the ".XML" and ".xml" extension in the URL.
     227
     228And...not to forget:
     229
     230Your warehouse_report is still a controller function, that means you can implement forms as usual (e.g. operated with form.accepts).
     231
     232To distinguish between different HTTP methods (in case you need that), you can use:
     233
     234http_method = jr.http # gives one of "GET", "POST", "PUT" or "DELETE"
     235
     236(...whatever PUT or DELETE would mean for a warehouse report, you would probably just ignore these)