difference between render and redirect_to

Wow. All those answers and no one seems to be able to just answer your question succinctly... or correctly for that matter.

Redirect means just what it says: send a redirect (status 302) back to the browser, telling it to perform a get request for the action indicated.

Render means just render the appropriate template and return html.

It is good form, when handling a POST request -- which is what "update" is doing -- to redirect the browser to the next page rather than simply returning html. If you return a html on a POST request, then the browser simply displays that page and, if the user clicks "reload" or "back", they get a confusing (to most people) alert from the browser that they're resubmitting a POST. Even worse, if the POST is submitted again and the server doesn't realize it's a repost, the action done in the POST (say, like buying a new computer... heh) could be done all over again!

I actually believe that *both* success and failure should redirect the browser. I think this is one glaring failure of the rails generators: they're not practicing what they preach completely. The problem is that after a redirect, it's a whole new request and the errors that were put in the model object are lost. You can get around this by storing the model object in the flash through the redirect, but that's probably more complicated than the generator writers wanted to deal with.

Hope that helps. If you still don't understand "render" and "redirect", you might want to do some reading on how http, html, and web apps work before tackling rails.

Ben

Chris Olsen wrote:

Ben Munat wrote:

I actually believe that *both* success and failure should redirect the browser. I think this is one glaring failure of the rails generators: they're not practicing what they preach completely. The problem is that after a redirect, it's a whole new request and the errors that were put in the model object are lost. You can get around this by storing the model object in the flash through the redirect, but that's probably more complicated than the generator writers wanted to deal with.

Seems to me that the "render" is ok in the case of failure because the effects of that request are null and void. If the user hits reload they will still get a inert action so no harm. Only if a failed action does cause some side effect would you need to do a redirect to ensure that a reload doesn't cause that side effect again (unless causing that side effect again is harmless). Plus as you said it simplifies displaying the error message.

Eric

Thanks for the input. That clears things up, although Ii was unaware
of issues flash session issues. Would anyone care to expand on this?

Hmm, how would that help? Unless you're thinking of issuing a
redirect that redirects to another redirect, etc., I don't think
having a "lifetime" on a flash object would be appropriate. Keep in
mind that if you can't guarantee which page the user will visit next,
you could end up presenting some odd behavior indeed.

(As it stands, I suppose someone could send another request, say in
another browser tab, between receiving the redirect and requesting the
target, but I guess that's unlikely enough to worry about.)

What I mean is that in order to ensure no problems with losing stuff
that you need to render various pages, you have to cover _every_ case
that can cause a re-render of a particular page, and make sure that the
appropriate flash key is set there.

I find using a session attribute much easier, but of course, this brings
up concerns about polluting the session with too much stuff, so that you
need to be vigilant about cleaning up after yourself.

If I understand correctly, couldn't you just check for a flash
variable at the point of instantiation. For example, in the old
(1.1.6) scaffold code, instead of:

  def new
    @thing = Thing.new
  end

  def create
    @thing = Thing.new(params[:thing])
    if @thing.save
      flash[:notice] = 'Thing was successfully created.'
      redirect_to :action => 'list'
    else
      render :action => 'new'
    end
  end

You'd have:

  def new
    @thing = flash[:thing] || Thing.new
  end

  def create
    @thing = Thing.new(params[:thing])
    if @thing.save
      flash[:notice] = 'Thing was successfully created.'
      redirect_to :action => 'list'
    else
      flash[:thing] = @thing
      redirect_to :action => 'new'
    end
  end

Similar for #edit/#update.

Doesn't strike me as much more complex; what it does require is that
your records are serializable.

Some other points:

I have a couple of apps. where 2 - 3 screens must be completed before I
even save a new object to the database. In these cases, I store the
new, unsaved item into the session to keep it available as it
accumulates state. But depending on how your object's dependencies look
and how much they are populated before being saved, you may have trouble
even using the session (even through the flash mechanism, since flash is
just session) to store these new, unsaved objects. This is because
there are limits on how big your session data is and since session is
serialized/deserialized between requests, this can cause a particular
action to fail due to inability to deserialize the session.

What's the limit on session size?

This may lead you to use a "form model" object whose sole purpose is to
hold the state of a form that needs to be persisted across requests so
that you can successfully re-render stuff but now have to try and store
so much into the session. There are other valid architectural reasons
that you would use a "form model" object as well. But the bottom line
is that you may find that you need to use an object just to manage UI
state that then dumps/reads data to/from your model objects.

See the ActiveForm plugin for a nice implementation of a "form model"
object that does ActiveRecord validation.

You could do it that way. You might also consider adding a state
column, and storing the record in the database with one of a few
'incomplete' states. Validations would fire only if they're relevant
for that particular state. This also gives the user the option of
coming back at a later date, or returning after a crash.

Just an idea.

George Ogata wrote:

  def new
    @thing = flash[:thing] || Thing.new
  end

  def create
    @thing = Thing.new(params[:thing])
    if @thing.save
      flash[:notice] = 'Thing was successfully created.'
      redirect_to :action => 'list'
    else
      flash[:thing] = @thing
      redirect_to :action => 'new'
    end
  end

That's how I do it... though you need to add code to the "new" and "edit" action to check for the model in the flash, and use that instead of creating/retrieving one.

Seems to work fine, though I suppose there are potential problems with putting an AR in the session... size/serialization issues.

I think it was Wes that was saying that he sometimes creates an AR just for a given form to deal with situations like that. Not crazy about adding a table just to handle a troublesome form, but I suppose that's not far off from storing sessions in the db.

Hmm, I wonder if one could hack rails redirects to use continuations? Not sure if that would work......

b

George Ogata wrote:
> def new
> @thing = flash[:thing] || Thing.new
> end
>
> def create
> @thing = Thing.new(params[:thing])
> if @thing.save
> flash[:notice] = 'Thing was successfully created.'
> redirect_to :action => 'list'
> else
> flash[:thing] = @thing
> redirect_to :action => 'new'
> end
> end
>
That's how I do it... though you need to add code to the "new" and
"edit" action to check for the model in the flash, and use that instead
of creating/retrieving one.

I thought I did... :wink:

Hmm, I wonder if one could hack rails redirects to use continuations?
Not sure if that would work......

Problem I see with that is that you'd need to have your continuation
be sharable between all your rails processes somehow (either that or
have something mediate which one(s) are able to handle the request).

Besides which, ruby continuations mightn't be around too long. At
least not on the main ruby implementations. [ http://rubyurl.com/c38 ]

Yes it does, thanks. Yeah, I don't think I'd be using the flash for
anything more than persisting things across a redirect.

Sorry, when I read "lifetimes" of a session variable, I figured you
meant, for example, "let this thing live in the flash through 3
requests". That sort of lifetime I think is not a good idea for the
same reason as persisting an object using flash across a form submit.
Not only does it mean your AJAX handlers have to restore the flash,
but you can't guarantee that the user will visit the intended action
next.

OTOH, if you meant "let this thing live in the flash until condition X
is met", then that could probably be made to work.

Hmm, yeah... I haven't had to do this on a form with an ajax call yet (I must confess that I've mostly done throwaway experiments... just starting on my first for-real, for-pay rails project)... Thanks for the heads up. Couldn't you use flash.keep for this situation?

b

Wes Gamble wrote:

George Ogata wrote:

Besides which, ruby continuations mightn't be around too long. At
least not on the main ruby implementations. [ http://rubyurl.com/c38 ]

Yeah, I'd heard about that... and I'm quite upset about it. How dare they remove continuations before I've had a chance to use them? :wink:

But seriously, I start to have grave doubts about the future of any language/platform when it starts *removing* features and breaking backwards compatibility. Even if it is a feature that hasn't been used that much.

b