[[TOC]] [wiki:S3XRC] | [wiki:S3XRC/RESTfulAPI S3 RESTful API] | s3_rest_controller = s3_rest_controller = == Introduction == The so-called '''REST Controller''' (function {{{s3_rest_controller()}}}) is a helper function to easily apply the RESTful API of the [wiki:S3XRC/RESTfulAPI/S3Resource S3Resource] class to your controller. {{{s3_rest_controller}}} does: - parse and execute the incoming HTTP request on the specified resource - populate and hand-over view variables - choose and set the response view template ({{{response.view}}}) Using {{{s3_rest_controller}}}, a basic RESTful controller for the {{{pr_image}}} table can look like: {{{ def image(): """ RESTful CRUD controller """ return s3_rest_controller("pr", "image") }}} This exposes all standard [wiki:S3XRC/RESTfulAPI/URLFormat URLs] and [wiki:S3XRC/RESTfulAPI methods] for this table, including: - interactive create, read, update, delete and list views - non-interactive data export/import (GET/POST/PUT/DELETE) in multiple formats == Basic Syntax == {{{ output = s3_rest_controller(prefix, resourcename) }}} - '''prefix''' is the application prefix of the resource - '''resourcename''' is the name of the resource (without prefix) - '''output''' contains the result of the request and can be returned from the controller as-is - in interactive view formats, this is a {{{dict}}} of view variables == Basic Options == All of the following options are set using: {{{ s3xrc.model.configure(table, key=value) }}} where: - '''table''' is the respective DB table - '''key''' is the setting key - '''value''' is the configuration value You can specify multiple settings at once: {{{ s3xrc.model.configure(table, key1=value1, key2=value2, ...) }}} '''configure''' overrides any settings that have been made for that table before (e.g. in the model), where only the specified keys are changed while all others are left untouched. You can also delete a particular setting by: {{{ s3xrc.model.clear_config(table, "key") }}} where "key" must be the respective key as ''string''. === CRUD Options === You can define which of the CRUD functions are allowed for your resource. By default, they all are True. {{{ s3xrc.mode.configure(table, editable=True, # Records can be updated insertable=True, # New records can be added deletable=True, # Records can be deleted listadd=True) # to have a add-record form in list view }}} Note: - these settings are completely independent from the user's permission to perform the respective actions. If you have set {{{insertable=False}}}, then adding records to this table will be impossible even if the user has the permission to add records. - in standard views, the add-form in list views is hidden and can be activated by clicking the respective "Add Record" button. If {{{listadd=False}}}, then the "Add Record" button will redirect to the create-form view instead. - these settings do not effect non-interactive representations === List-add === By default, list views will contain a hidden create-form and an "Add Record"-button to activate the create-form. When you set {{{s3xrc.model.configure(table, listadd=False)}}}, the "Add Record"-button will link to a separate create-view instead. If the resource is set to {{{insertable=False}}} or the user is not permitted to create records in this table, no "Add Record"-button will be available. To generally hide the "Add Record"-button (e.g. table can only contain one single record), use: {{{ s3_rest_controller(prefix, resourcename, add_btn=None) }}} === Redirection === Default destination of the redirection after a ''create'' or ''update'' is the ''read'' view of the processed record, after a ''delete'' it is the ''list'' view of the respective table. The redirection destination can be configured per DB table, using: {{{ s3xrc.model.configure(table, create_next=url) }}} {{{ s3xrc.model.configure(table, update_next=url) }}} {{{ s3xrc.model.configure(table, delete_next=url) }}} where: - '''table''' is the respective DB table - '''url''' is the URL to redirect to If, for create_next or update_next, '''url''' contains the string "[id]" (or its URL-encoded equivalent), then this string will be replaced by the ID of the updated/newly created record before redirection. Note: - redirection does not happen after non-interactive data imports! === Callbacks === For every DB table, you can define functions to be invoked upon certain CRUD events. Those "callbacks" can be: - a single callable (function, lambda, callable object) - a list of callables, which are invoked in list order - a dict of callables, where the tablename is used as key to find the callable to be invoked - a dict of lists of callables, where the tablename is used as key to find the list of callables to be executed in list order The return value of the callables, if any, is ignored. '''Important:''' Callbacks are invoked in the same manner during non-interactive data imports, where usually multiple records will be processed in one and the same request. Therefore, any callbacks '''must not redirect'''! ==== Validation Callbacks ==== You can define extra form validation methods to be invoked after a create/update form has successfully passed the indivdual field validation, by using: {{{ s3xrc.model.configure(table, create_onvalidation=callback) }}} {{{ s3xrc.model.configure(table, update_onvalidation=callback) }}} where: - '''table''' is the respective DB table - '''callable''' is the callback setting, see [#Callbacks Callbacks] If either of {{{create_onvalidation}}} or {{{update_onvalidation}}} is not set, then the {{{onvalidation}}} setting is tried: {{{ s3xrc.model.configure(table, onvalidation=callback) }}} This allows you to define a common onvalidation callback for both ''create'' and ''update''. Onvalidation callbacks are meant to allow additional form data validation (beyond individual field validators). The callback functions receive the '''form''' as first and only parameter, while their return values will be ignored. Any validation errors are to be reported directly into the form as: {{{ form.errors[fieldname] = error_msg }}} where: - '''fieldname''' is the field containing the invalid value - '''error_msg''' is the error message to be displayed in the form close to that field If after the execution of the onvalidation callback any messages are found in {{{form.errors}}}, then no data are being imported and instead, the process will return to the input view with the messages displayed close to the respective form fields. In non-interactive data imports, the error message will be added to the import tree as extra attribute of the invalid element. The XML importer will however process all records in the import tree in order to find all validation errors before reporting the invalid tree to the sender, and in case {{{ignore_errors}}} is used, all valid records will be imported in the first attempt. ==== On-accept Callbacks ==== You can define methods to be invoked after a record has been created/updated, by using: {{{ s3xrc.model.configure(table, create_onaccept=callback) }}} {{{ s3xrc.model.configure(table, update_onaccept=callback) }}} where: - '''table''' is the respective DB table - '''callable''' is the callback setting, see [#Callbacks Callbacks] If either of {{{create_onaccept}}} or {{{update_onaccept}}} is not set, then the {{{onaccept}}} setting is tried: {{{ s3xrc.model.configure(table, onaccept=callback) }}} This allows you to define a common onaccept callback for both ''create'' and ''update''. The {{{onaccept}}} callbacks are meant to perform extra post-processing of the newly created/updated record (e.g. to update dependent records). The callback functions receive the respective '''record''' as first and only parameter, while their return value will be ignored. ==== On-Delete Callback ==== You can define methods to be invoked after a record has been deleted, by using: {{{ s3xrc.model.configure(table, ondelete=callback) }}} where: - '''table''' is the respective DB table - '''callable''' is the callback setting, see [#Callbacks Callbacks] The {{{ondelete}}} callbacks are meant to perform extra post-processing of the deleted record (e.g. to update dependent records). The callback functions receive the respective '''record''' as first and only parameter, while their return value will be ignored. Note: At the time when the callback is invoked, the record is already deleted from the database. === Pagination === The default pagination method is '''server-side''' (SSPag), meaning, in list views the client will receive only the first of the available rows, and then retrieve more rows as needed by subsequent Ajax calls. In contrast to that, in '''client-side''' pagination (CSPag) mode all available rows of the list are retrieved and send to the client at once. For most tables, though, this will probably be a huge data set and take a long time to extract and transmit, while mostly being unnecessary when the user only needs to see the first 20 rows to find what he's looking for. However, some tables may by their nature only contain one or few rows, and then server-side pagination is not needed (in fact, inefficient). In these cases, the respective controller can turn it off by: {{{ response.s3.no_sspag=True }}} == View Control == In web2py, the default view is chosen after the name of the controller, i.e. if the controller is person(), then the default view is person.html. s3_rest_controller() modifies this schema in order to allow you to create method-specific views for the same controller. === Default View === The default view is chosen in a fallback cascade, which is: 1. /__.html 2. /_.html 3. .html where: - '''prefix''' is the application prefix of the resource name (e.g. "pr") - '''resource-name''' is the name of the resource (e.g. "person") - '''component-name''' is the name of the component resource (e.g. "address", only if the request targets a component resource) - '''method''' is one of: - display - list - list_create (to be deprecated) - create - update - delete - search Example: {{{ http://localhost:8000/eden/pr/person/create.html }}} is looking for one of '''pr/person_create.html'', '''pr/create.html'' or finally ''create.html'' as default view. If none of these is found, "default.html" serves as catch-all fallback. === Custom View === To choose a custom view, you can easily override the default setting ''after'' s3_rest_controller returns: {{{ output = s3_rest_controller(prefix, resourcename) response.view = "myview.html" return output }}} === Additional View Variables === In interactive view formats, any additional named arguments in the {{{s3_rest_controller}}} argument list will be added to the view variables: {{{ output = s3_rest_controller(prefix, resourcename, **attr) }}} - '''**attr''': additional view variables - any callable argument will be invoked with the {{{S3Request}}} as first and only argument, and its return value will be added to the view variables - any non-callable argument will be added to the view variables as-is - any argument that gives {{{None}}} will remove this key from the view variables A typical use-case is '''rheader''': {{{ def my_rheader(r): if r.interactive and r.component: # Code producing the rheader... return rheader else: return None output = s3_rest_controller(prefix, name, rheader=my_rheader) }}} If {{{my_rheader(r)}}} gives something else than {{{None}}}, then this value is added as {{{rheader}}} to the view variables. == Advanced Options == === Pre-Process === - ''coming soon...'' ==== Passing information between main controller & pre-processor ==== Scope normally means that these 2 sections can only talk to each other via globals or the Request object. If you need to pass data between them, you can use this trick: {{{ vars = {} # the surrounding dict def prep(r, vars): vars.update(x=y) # the actual variable to pass is x return True response.s3.prep = lambda r, vars=vars: prep(r, vars) output = shn_rest_controller(module, resource) x = vars.get(x, None) }}} An example usage is in {{{controllers/gis.py}}} for location() === Post-Process === === Method Handlers === === Custom Methods === ---- DeveloperGuidelines