views:

137

answers:

2

OK, I am trying to understand the best practices for the CREATE and UPDATE methods for both HTML and XML formats. The default code for a controller that the rails generator generates is a little unclear to me.

For the CREATE method, given a good save, the generator says to "redirect_to(@whatever)" for HTML and "render :xml => @whatever, :status => :created, :location => @whatever" for XML.

Given a bad save, the generator says to "render :action => 'new'" for HTML and "render :xml => @whatever.errors, :status => :unprocessable_entity" for XML.

However, for the UPDATE method, given a good update, the generator says to "redirect_to(@whatever)" for HTML and "head :ok" for XML.

And, given a bad update, the generator says to "render :action => 'edit'" for HTML and "render :xml => @whatever.errors, :status => :unprocessable_entity" for XML.

I understand this, and it makes sense to me, and works just fine - BUT, I have two questions:

First, for a successful CREATE and UPDATE, HTML format, why "redirect_to(@whatever)" instead of "render :action => 'show'"? I understand the differences between redirect and render, just more curious about which way you guys tend to do it and why. It seems the redirect would be an unnecessary extra trip for the browser to make.

Second, why "head :ok" upon successful UPDATE via XML, but "render :xml => @whatever, :status => :created, :location => @whatever" upon successful CREATE via XML? This seems inconsistent to me. Seems like a successful UPDATE via XML should be the same as a successful CREATE via XML. Seems like you would need the new/updated object returned so you could test it. How do you guys do it and why?

+1  A: 

On create or update, redirect_to(@whatever) to clear the post, so that the user doesn't resubmit by refreshing. It also shows the correct url in the address bar for the create case, which posts to the collection path (/whatevers).

head :ok makes a minimal response on update, when usually you would already have the object in the dom. If you are updating the page after an update, the standard method is to use rjs views to update dom elements and render partials.

Sam C
I agree with your answers for #1, but i'm not so sure about #2. Like Sidane says below, the object could be changed by callbacks. Also, because of the odd "silently ignoring" default behavior of update_attributes, sometimes it will return true even when some invalid keys or values are passed to it (or if some attributes are protected or read-only). In this case, the object you had in the DOM is different than how the object really looks in the backend after the update. "Head :ok" might be OK, but for testing purposes I think I might want to have a copy of the real object after the update.
Buddy
A: 

I'd already written this out when Sam C replied, but here it is anyway :-)

For the first part - why redirect instead of render? Two reasons I can think of:

1) It's consistent. If you rendered the show action and the user uses the back button later on return to that page, the user will see unexpected behaviour. Some versions of IE will give you some kind of session timeout error IIRC, other browsers may handle it a bit more gracefully.

Same goes for if the user bookmarked that page and returns to it at a later date using a GET request - they won't see the show action. Your app may throw an error or may render the index action because the user is requesting a URL like http://my.app.com/users, which would map to the index action when using a GET request.

2) if you render the show action without redirecting to a GET request and the user hits refresh, your browser will re-submit the POST request with all the same data, potentially creating duplicate instances of whatever it is you were creating. The browser will warn the user about this so they can abort, but it's potentially confusing and an unnecessary inconvenience.

As for the second part of your question, not too sure to be honest. My guess is that since you are already updating the object in question, you already have a copy of it so do not need another instance of it returned. Having said that, updating an object could trigger various callbacks which modify other attributes of the object, so returning that updated object with those modifications could make sense.

Sidane
I agree with your answers to the second part of my question. And your answers to the first part of my question make sense, but then that begs another question: Why, after an unsuccessful create or update, is render used and not redirect_to for the same reasons you've mentioned? What if users wanted to bookmark the edit or new page? If render was used, then they would still be on the "/whatevers" page (which is wrong).
Buddy
If the user were redirected to the new/edit action, all the POST data submitted would be lost and the rendered form would be blank. You are correct that the user could bookmark the rendered error page and it would point to the wrong URL. No way to prevent this AFAIK.
Sidane
Oh yes, duh. I incorrectly thought you could redirect_to the new or edit page AND pass an @object. I was wrong. Thanks for setting me straight!
Buddy