On Mon, Sep 8, 2008 at 1:46 PM, Steve M <[EMAIL PROTECTED]> wrote:
>
> RESTfulness in Pylons
> ---------------------
>
> I've been trying to buld a web application in a RESTful fashion, using
> for guidance among other resources the Leonard Richardson and Sam Ruby
> book _RESTful Web Services_. I've learned that REST is a style of
> architecting applications, and that Pylons with the map.resource()
> function of routes can be seen as one particular way of defining and
> working within some RESTful constraints.

map.resource follows the Atom specificiation, which is a particular
implementation of REST.  It's good for resources that can be
serialized or accessed via web services.  It's possibly overkill for
web-only resources, especially those that don't implement some of the
modes (index, delete, add).

The main problem with map.resource is that there's no URL symmetry
between the form and its action, yet unrelated actions are doubled up
under the same URL.  This is confusing to remember, though it makes
sense for serializable/web service resources.

The main feature of REST is that each URL component should contain the
one on its right, and IDs generally come before actions.  So for
instance, one of my sites has a combined details page with tabbed
sections, but a separate edit form for each section.  So the display
URL is /sites/N, but the forms are /sites/N/mytab/edit .  (Adding is
/sites/add; deleting is not allowed.) This is not Atom-compliant but
it is RESTful.

A simple /resources/add, /resources/N/edit, and /resources/N/delete
scheme, where GET displays the form and POST does the action, would
also be RESTful.  I want to add another resource type to Routes for
this, but it won't be anytime soon.

> Perhaps the "GET /messages" index includes for each resource in the
> collection a form with a button that allows the client to send an HTTP
> DELETE to that message's URL. So if there are ten messages in the URL,
> there will be ten items returned by the index action, and those will
> include ten forms, one to send a DELETE to each of the ten messages.

Yes, but the primary purpose of "GET /messages" is to show links for
viewing the resources.  Delete buttons can go alongside these, but
should not replace them.

> How do you implement batch operations on collections of resources?
>
> Imagine you have a resource Messages. If you "GET /messages" it
> returns an index of messages.
> If you "DELETE /messages/4" it will delete message 4.
>
>
> Now, suppose you wish to have a way for the user to do a batch delete
> on a subset of messages. The traditional (non-RESTful) way to achieve
> this is to have the list of ten messages inside a single form, with a
> checkbox for each list item. The Delete button would submit an
> overloaded POST to some URL such as "POST /messages/delete" and the
> server can delete all the checked off items. Or, alternatively, there
> will be an even more generic URL, "/messages/batch" and the fact that
> the selected messages are to be deleted or moved to a different folder
> will be specified as an input element of the form. But of course
> neither of these is available in the current RESTful framework. The
> way to delete a set of five different messages is to send five HTTP
> requests, each of the form "DELETE /messages/:id".

There is no REST syntax for batch operations.  So do whatever's
easiest.  I would put all the data in POST variables.  The purpose of
RESTful URLs is so the user can bookmark them and type them, and
non-human agents can manage the resource without having to know the
site's esoteric syntax.  None of these apply to batch operations,
which are normally ad hoc (i.e., not repeatable).  So

    /messages/batch/delete?id=1&id=2

would be acceptable, or

  /messages/batch?action=delete&id=1&id=2

(but using POST of course, since GET is not supposed to have
destructive consequences.)

You can define these routes within map.resource or as separate routes.

You can use request.params.getall("id") to get a list of all id
parameters, and url_for("batch", id=[1, 2]) to go the other way.

> 1. Use AJAX techniques to submit the five requests for the user.
> Basically have the form submit intercepted by a javascript which fires
> off five asynchronous HTTP DELETE requests to the five URLS and
> collects the responses for display. This requires no changes to the
> server API. If the client does not have javascript, the interface can
> degrade gracefully to requiring a separate submit button click for
> each delete.

AJAX is outside the realm of URL design.  Use it if you like it or it
improves latency.

> 2. Of the seven REST actions, four of them (show, edit, update,
> delete) act upon individual resources. Extend these to be able to act
> on collections of resources, and therefore also generalize the URL
> formats to allow for addressing subsets of collections. For example,
> the URL "/messages/3,4,18,22-50" refers to a subset of the collection
> of messages. An HTTP DELETE to that URL would delete each resource in
> that subset.

You could write your own convention for this, but I don't know if
there's any standard.  I don't think map.resource requires the ID to
be numeric, so it could have embedded commas or hyphens.

> In this case, of the remaining 3 REST actions that act only upon
> collections, index, new, and create, one of them, index, would be
> updated to act on proper subsets of the collections, and the other two
> would behave the same whether the URL refers to the entire collection
> or some subset.

map.resource does not use NEW or CREATE.  It uses POST to create a
resource, and PUT to modify it.  Note that since POST is defined as
"do an arbitrary action", it's ambiguous enough to accommodate batch
operations.

> One big challenge of this approach is creating a scheme for addressing
> arbitrary subsets of a collection, and by addressing I mean having it
> in the URL. The scheme would have to be such that the URLs could be
> created using HTML forms. Finally, after developing the scheme it
> would also require significant changes to routes to support it.

I don't think it would require changes to Routes.  You just have to
put routes with a keyword above those that have an ID in the same
position.  The id would come in as a single string, which the
controller action would have to parse.  You could make a fork of
Routes that splits batch operations into single ones, but it won't be
widely adopted because it's not a standard.  And the controller action
itself may pay prefer to get all the IDs together anyway so it can do
a bulk database query.

> 3. Use overloaded POST to create new REST actions in addition to the
> basic seven that are meant to support batch operations. For example,
> three new actions called batch_create, batch_update, batch_delete. In
> this case it is not necessary to address subsets in the URL; the
> collection URL for a resource can be used instead. However support in
> routes map.resource might be required to route the incoming POST to
> the proper RestController action. The POST request body for each of
> those actions can contain the set of resources and representations to
> act upon.

This is essentially the same as my proposal above, just with a different syntax.

-- 
Mike Orr <[EMAIL PROTECTED]>

--~--~---------~--~----~------------~-------~--~----~
You received this message because you are subscribed to the Google Groups 
"pylons-discuss" group.
To post to this group, send email to [email protected]
To unsubscribe from this group, send email to [EMAIL PROTECTED]
For more options, visit this group at 
http://groups.google.com/group/pylons-discuss?hl=en
-~----------~----~----~----~------~----~------~--~---

Reply via email to