Updating multiple RESTful records in one request

I like REST, it generally makes sense and is pretty easy to follow.

Where I’m having trouble though is when it comes to the point where
you need to update multiple records.

Say for instance you have an email application, which is RESTful.

Now lets say you go to your inbox and select 5 emails and you want to
mark them all as read.

It would be easy enough to create a custom action that would process
this kind of request, but is there a good way to do it while
maintaining REST?

I asked this very
questionhttp://groups.google.com/group/rubyonrails-talk/browse_thread/thread/5b8e9e2d0b77c1/73000d78df51ffe7?lnk=gst&q=multiple+rest&rnum=2&hl=en#73000d78df51ffe7about
a week ago and didn’t get a definitive answer. The consensus was that
I was missing a model somewhere. That answer didn’t really work for me
(since I was RESTifying a production app and couldn’t change it too
extremely). So I have routes like:

map.resources :emails, :collection => { :archive => :post, :destroy =>
:delete }

and then, in your EmailsController#destroy, you need to handle multiple
email ids. It’s a hack, but it can work.

ed

Thanks for the reference to the previous discussion and your choice of
solution.

I’m designing a new app luckily, and I’m still in the “making pieces
fit together” stage of things.
So I have a lot more liberty in my data modeling.

I think I understand well enough to try and put something together.

I suppose I just need to treat the “mark multiple emails as read”
action as a seperate model.
Then you can restfully create batch requests.

Seems like a round about way to do it, but maybe I just need to let
the idea sink in better…

I still tend to agree.

It might be great to wrap your transactions in a model of some sort if
you need to do auditing or other things, but most apps don’t need the
extra overhead.

There are certainly situations where the “you missed a model” answer
is probably quite true, as DHH did when he introduced a lot of this
stuff. In that case it did make sense to squeeze another model in the
middle to solve the problem.

I don’t necessarily think that the sort of processing we are talking
about fits into that same notion though. Or at least I remain
unconvinced.

That doesn’t mean that I have some shining light or better way either,
unfortunately.
It just means I haven’t figured this out yet (and I suspect I’m not
the only one).

Short a specific answer to that very common problem I would be
disinclined
to agree that you’re missing a model to represent a collection of
accounts.
If it’s clean and clear and there’s a convention there it should’ve been
easy to get an answer to your very simple and fair question about
updating
multiple records.

That sounds entirely logical to me.

I would agree that using the class method is a good way to implement
it too.

The problem is that, by my understanding, you would be breaking out of
the REST paradigm.
REST aims to make it such that the only actions are create, read,
update and delete.

As per DHH in the 1.2
announcement

“Then start thinking about how your application could become more
RESTful. How you too can transform that 15-action controller into 2-3
new controllers each embracing a single resource with CRUDing love.”

REST uses nouns, not verbs. So having other actions than CRUD (which
can use the same noun, at least in theory, by using HTML PUT, GET,
DELETE) is against the RESTful rules.

I want to be able to click a button and have it submit several
requests to the same action. That is what a batch controller or an
external application would do.
There just doesn’t seem to be a good, direct way to do this (that I
know of) within a webpage…

You don’t necessarily need a separate model to handle operations on
multiple records.

I’ve got a few places in my app where I need to perform an action on a
group of related records, and the best way I’ve found is to use class
methods. You could simply define a Message.mark_as_read(message_ids)
method in your message class. Class methods are useful when you need
to do table-level (as opposed to row-level) operations. That way,
you’re keeping all the logic for marking multiple messages as read in
the model, where it belongs, and your controller can simply have an
‘archive’ action to call it and pass it the array of message_ids.

I had a similar problem…

I wanted an action to connect a product to it’s categories…

So the web user can pick several categories from a list and assign it to
the
current product

This means you have two different model classes, one single product and
several categories that should be connected by a m:n relationship. how
could
you model something like that in crud?

I find the idea of having a ProductCategoriesController a little awkward
:frowning:

Having an action set_categories inside the ProductController that
accepts a
list of categories seems logical but doesn’t fit into crud either.

My personal opinion is that REST has it’s limits and is no replacement
for
bulky controller actions and routes sigh

2007/2/7, monki [email protected]:

oops sorry… that route is:

/inbox/37/email_batch/1;read

or whatever:-)
Tim

Just curious what you guys think of this:

Why not create a transient (no db table) model named EmailBatch or
whatever. Conceptually it would always exist and it’s id is always 1
so you’d always do PUT/update operations. You simply initialize it
with whatever group of emails you need to deal with at any given
moment.

Then have an EmailBatchesController with routes like the following.

/inbox/email_batch/1;read

or whatever.

Did that make sense?

Tim

The thing that’s helped me the most in refactoring to REST is
understanding what Nouns need to be accessible externally. My RESTful
controllers support the basic CRUD operations, and then, as needed
have additional operations for the in-browser user experience. In
this case, I’d say that “mark selected emails read” is the sort of
thing that a user interacting with the application via a web browser
would like to have, but it probably doesn’t matter to an XML REST
client consuming your site externally. So, in that case, I don’t mind
having a controller with 9-12 actions where the extra actions support
the user experience.

If you’re finding that you want to view the mailbox externally, check
off messages, and mark them as read over REST, the client can do some
of the lifting for you, in which case that client will loop over the
IDs and make separate PUT calls to change the has_been_read status
from false to true.

Seems like a lot of bending over backwards for a new, shiny concept
like REST when a simple “each” loop and set of checkboxes works fine.

I predict that, like so many other design fads, REST will be a
“remember that thing?” 10 years from now. Don’t kill yourself for it.
Get as close as you can.

-Nate

I don’t know if I’d go so far as to suggest that REST will wind up
completely forgotten… there will always be certain cases where we want
machine-to-machine communication.

However, I do smell a little bit of shiny-new-thing-itis… I would
throw out the question: how many people playing (struggling?) with
RESTful rails actually have a machine-to-machine use case? Are most
people adding REST services “just in case”?

b

PS: My current project is currently fleshing out an application with
very heavy REST usage, so I don’t mean to come off as a luddite here.

ChrisR

One of the goals of REST is to surface your state-engine events as
verbs on the underlying nouns.
Yes. I agree, but I think the ‘state machine’ description is useful.

One of the goals of REST is to surface your state-engine events as
verbs on the underlying nouns.
I agree, but having custom verbs is also not good.
One of the main goals of the REST model is consistancy across models.
I think a lot of this type of state-to-state transition can be covered
with simple updates.
If you need more data than that you may need a has_many through sort
of thing.

As he climbs wearily down from his soap-box, “Oh yeah, it only works
for a single entity life-cycle, so class methods are good for small
batches”.
This is definately the case. I think I have the pieces together in my
mind now.
All single model updates (the stuff that would be accessible both
externally and internally) probably ought to be restful.
If you need to do stuff like batch processing you can add extra
options/actions, and it won’t break REST.

Thats because when you add these external options for internal use you
are really building a client,
which just happens to reside on the server due to the nature of the
web.
So, go wild but keep it to yourself.
Which, I think is what Jared was suggesting.

As for Nate and Ben.
I think that REST is useful and there is a growing need for machine-to-
machine interaction.
BUT, I think people are misunderstanding what that entails and going
overboard or “bending over backwards” to try and fit round pegs into
square holes.

Lots of discussion, so a summary of my thoughts:

Use/Expose a RESTful interface to each of your models, to handle all
the requests to manipulate single objects.
More complex and batched commands can exist outside the REST model, in
order to provide client-type functionality.
These complex functions need not be exposed (each client should
provide their own utility functions), and for consistancy
they should use the REST methods to actually do the meat of the work
(therefore decoupling/drying up the actual model manipulation from
other complex logic).

Hopefully that makes some sense, I at least feel like I have a much
firmer grasp of what I’m up against.
I think it may take me a bit more time to really put it down in a
succinct, elequent manner…

On Feb 8, 5:43 am, “monki” [email protected] wrote:

That sounds entirely logical to me.

REST uses nouns, not verbs. So having other actions than CRUD (which
can use the same noun, at least in theory, by using HTML PUT, GET,
DELETE) is against the RESTful rules.

Just a point of clarification of the concept of REST (at least the one
that is inside my head).

REST .NEQ. CRUD

REST uses verbs acting on nouns.

Simple entities with trivial life-cycles can be handled with CRUD.
They are either there or they are not. In business analysis terms,
these are usually classifiers or look-ups (e.g. sales area, message
type,…)

More interesting entities have a richer life-cycle typically marked
with a status field.
(e.g. Mail message={Unread,Read,Archived}, WorksOrder =
{Booked,Scheduled,In Progress, Completed,Billed} )

Now moving from one status to another is a life-cycle event and often
there is extra data involved.
So the ‘Read’ event for the mail-message moves it from ‘Unread’ to
‘Read’ and may involve populating the read_date column.
The ‘StartWork’ event for the WorksOrder moves it from the
‘Scheduled’ to ‘In progress’ state and may involve populating the
start_date and also adding work-queue information to other parts of
the data model.

This concept is very well documented in its variants as ‘finite state
machine’, ‘state transition diagram’,‘state machine’,‘state
engine’,‘life-cycle model’,…

One of the goals of REST is to surface your state-engine events as
verbs on the underlying nouns.

As he climbs wearily down from his soap-box, “Oh yeah, it only works
for a single entity life-cycle, so class methods are good for small
batches”.

So what’s the point?

REST is good for simple stuff … and that’s it???

just another hype/buzzword?

my current summary (with the ‘mark emails as read’ example):

  • I don’t think it makes sense to iterate several emails in the
    browser to mark them unread (c’mon, one http request for each mail,
    you gotta be kidding :slight_smile:

  • having additional nouns/verbs that affect more than one model type
    or instance seems not to fit into REST

  • I don’t think a ‘EmailBatch’ can necessarily be seen as a model

  • having an extra so called ‘batch-processor’ for every little
    operation consisting of more than one item or whatever sounds
    ridicuous IMHO

  • it seems that most people think REST ist cool but they all just
    struggle with it. so REST is only useful for the most simple of cases
    (create, read, update, delete) but gives you a headache if you need
    more…

true?

<…>

  • it seems that most people think REST ist cool but they all just
    struggle with it. so REST is only useful for the most simple of cases
    (create, read, update, delete) but gives you a headache if you need
    more…

true?

False :slight_smile:

Regards,
Rimantas

http://rimantas.com/

just another hype/buzzword?
I think it is useful for structuring server side things, it just
doesn’t cover all the needs of a client.

  • I don’t think it makes sense to iterate several emails in the
    browser to mark them unread (c’mon, one http request for each mail,
    you gotta be kidding :slight_smile:
    I could go either way. Iterating over them doesn’t really seem
    intolerable to me,
    and treating them as individual is cleaner and more regular than
    somehow modifying multiples.
  • having additional nouns/verbs that affect more than one model type
    or instance seems not to fit into REST
    I would still say this is the case, I’m not sure what you were saying
    about them though.
    I think it is important to be consistant so it would be better to keep
    it limited to exposed CRUD operations when possible.
  • I don’t think a ‘EmailBatch’ can necessarily be seen as a model
    I agree here. I think it would be terrible case of premature
    extraction,
    but unfortunately a lot of people seem to be trying to head down that
    road.
    Or at least to use “find an extra model” as an easy way to answer a
    hard question.
  • having an extra so called ‘batch-processor’ for every little
    operation consisting of more than one item or whatever sounds
    ridicuous IMHO
    I agree, hence why I feel a request per item probably makes more
    sense.
  • it seems that most people think REST ist cool but they all just
    struggle with it. so REST is only useful for the most simple of cases
    (create, read, update, delete) but gives you a headache if you need
    more…

true?
I don’t think that it should be disregarded simply because it is hard
or misunderstood.
I think it is good to shift the focus to a resource driven
architecture, as this is often
a better/cleaner solution. I just think it is folly to try and fit
everything within this context,
there are some things that are better off being outside this clean
little package. The
kinds of things that an independant client would do to aide user
interaction still need to be
done, and I feel that it is in this area that REST alone falls short.

On 15 Feb., 15:46, “monki” [email protected] wrote:

somehow modifying multiples.

I grew up with 8 bit computers where people cared about resources.
creating probably
houndreds of http(s) requests to mark 250 spam email for deletion is
just so horrible
that I must stop breathing… :-((

  • I don’t think it makes sense to iterate several emails in the browser
    to mark them unread (c’mon, one http request for each mail,
    you gotta be kidding :slight_smile:
    I could go either way. Iterating over them doesn’t really seem
    intolerable to me, and treating them as individual is cleaner and more
    regular than somehow modifying multiples.

Yes, separate HTTP requests for the bulk actions seems a bit overkill to
me. My solution for these multi-item actions was to just bastardize the
REST methodologies and allow the method to be passed multiple ids in the
query string. I then iterate over then in the code.

  • I don’t think a ‘EmailBatch’ can necessarily be seen as a model
    I agree here. I think it would be terrible case of premature extraction,
    but unfortunately a lot of people seem to be trying to head down that
    road. Or at least to use “find an extra model” as an easy way to answer a
    hard question.

I also agree. For some cases, a new model makes sense, but for many
things
(like EmailBatch) it seems unnecessary. So great, you’re following the
REST
ideals but now your breaking other good coding methods and making things
generally more complicated! For me, I think it will just take time to
understand the intricacies of REST and the best ways to handle the issue
that arise when coding under the inherent constraints.

-ed