92 | | However - there are plenty of possibilities. |
93 | | |
94 | | Dominic |
| 92 | Let me give you another recipe: |
| 93 | |
| 94 | Imagine you have a resource "warehouse" and want to implement a function "report" that generates a report about the current status of the warehouse. |
| 95 | |
| 96 | Now 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 | {{{ |
| 99 | http://site.myserver.org/eden/wm/warehouse/report |
| 100 | }}} |
| 101 | |
| 102 | and is provided in several different data formats beyond HTML (the interactive view), let's say - XLS and XML: |
| 103 | |
| 104 | {{{ |
| 105 | http://site.myserver.org/eden/wm/warehouse/report.xls |
| 106 | http://site.myserver.org/eden/wm/warehouse/report.xml |
| 107 | }}} |
| 108 | |
| 109 | How to tackle this? |
| 110 | |
| 111 | Yes - you can use shn_rest_controller for this! |
| 112 | |
| 113 | This could be your "warehouse" controller: |
| 114 | |
| 115 | {{{ |
| 116 | def warehouse(): |
| 117 | """ RESTful CRUD controller """ |
| 118 | return shn_rest_controller(module, resource, ...) |
| 119 | }}} |
| 120 | |
| 121 | At 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 | {{{ |
| 124 | def 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 | |
| 130 | def warehouse_report(jr, **attr): |
| 131 | """" Warehouse report """ |
| 132 | <Code to produce the report> |
| 133 | return report |
| 134 | }}} |
| 135 | |
| 136 | How 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 |
| 137 | are then formatted as HTML in the view template: |
| 138 | |
| 139 | {{{ |
| 140 | def 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 | |
| 148 | Note 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 |
| 149 | available to the view templates. That also means: do not use "jr" as key in that dict. |
| 150 | |
| 151 | However, 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 |
| 152 | found in jr: |
| 153 | |
| 154 | {{{ |
| 155 | def 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 | |
| 171 | See [wiki:S3REST#S3RESTRequest S3RESTRequest] to find out more about "jr". |
| 172 | |
| 173 | To 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 | {{{ |
| 176 | def warehouse_report(jr, **attr): |
| 177 | ... |
| 178 | elif jr.representation in shn_xml_export_formats: |
| 179 | return export_xml(xrequest) |
| 180 | ... |
| 181 | }}} |
| 182 | |
| 183 | Perhaps you want to add an RSS feed: |
| 184 | |
| 185 | {{{ |
| 186 | def warehouse_report(jr, **attr): |
| 187 | ... |
| 188 | elif jr.representation == "rss": |
| 189 | <Code to produce the RSS report> |
| 190 | return report |
| 191 | ... |
| 192 | }}} |
| 193 | |
| 194 | Ah - 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 | |
| 196 | This URL addresses the report for all warehouses: |
| 197 | {{{ |
| 198 | http://site.myserver.org/eden/wm/warehouse/report |
| 199 | }}} |
| 200 | |
| 201 | This URL addresses the report for the warehouse record with ID=1. |
| 202 | {{{ |
| 203 | http://site.myserver.org/eden/wm/warehouse/1/report |
| 204 | }}} |
| 205 | |
| 206 | This 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 | {{{ |
| 208 | http://site.myserver.org/eden/wm/warehouse/report.xls?warehouse.uid=123654278 |
| 209 | }}} |
| 210 | |
| 211 | The S3RESTRequest provides the resource information to your warehouse_report function. |
| 212 | |
| 213 | In case a specific record has been requested, you can access it as: |
| 214 | {{{ |
| 215 | record = jr.record |
| 216 | }}} |
| 217 | If 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 | }}} |
| 224 | instead. |
| 225 | |
| 226 | NOTE: representation in jr is always all lowercase, there is no differentiation between the ".XML" and ".xml" extension in the URL. |
| 227 | |
| 228 | And...not to forget: |
| 229 | |
| 230 | Your warehouse_report is still a controller function, that means you can implement forms as usual (e.g. operated with form.accepts). |
| 231 | |
| 232 | To distinguish between different HTTP methods (in case you need that), you can use: |
| 233 | |
| 234 | http_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) |