Forum: Ruby on Rails difference between render and redirect_to

Announcement (2017-05-07): www.ruby-forum.com is now read-only since I unfortunately do not have the time to support and maintain the forum any more. Please see rubyonrails.org/community and ruby-lang.org/en/community for other Rails- und Ruby-related community platforms.
Chris O. (Guest)
on 2007-02-02 07:12
In the scaffold created code it seems to be common to have something
like the following:

def update
    @product = Product.find(params[:id])
    if @product.update_attributes(params[:product])
      flash[:notice] = 'Product was successfully updated.'
      redirect_to :action => 'show', :id => @product
    else
      render :action => 'edit'
    end
  end


where a redirect_to is done on success and render on failure.  I can't
figure out what is the difference between them.  I have tried both in a
simple example and on success it worked fine with the "redirect_to" but
crashed using the "render".

If anyone can make this a little clearer for me I would appreciate it.

Thanks for the help.
rubian (Guest)
on 2007-02-02 07:17
Chris O. wrote:
> In the scaffold created code it seems to be common to have something
> like the following:
>
> def update
>     @product = Product.find(params[:id])
>     if @product.update_attributes(params[:product])
>       flash[:notice] = 'Product was successfully updated.'
>       redirect_to :action => 'show', :id => @product
>     else
>       render :action => 'edit'
>     end
>   end
>
>
> where a redirect_to is done on success and render on failure.  I can't
> figure out what is the difference between them.  I have tried both in a
> simple example and on success it worked fine with the "redirect_to" but
> crashed using the "render".
>
> If anyone can make this a little clearer for me I would appreciate it.
>
> Thanks for the help.




Render has something to do with just rendering that part of the view
without actually running the method again. Redirect actually takes you
to the page and does stuff from scratch.
askegg (Guest)
on 2007-02-02 07:30
(Received via mailing list)
On Feb 2, 4:12 pm, Chris O. <removed_email_address@domain.invalid>
wrote:
>     end
>
> --
> Posted viahttp://www.ruby-forum.com/.

render :action => "whatever" seems to be a shortcut to render a view;
ei.  it does not exectute the "whatever" method in the controller.
This is usefull if you want to pass session variables to a different
view without the hassle of going through a redirec as that will causes
the browser to issue another request to the server, which is not
really needed.
Benoit B. (Guest)
on 2007-02-02 08:19
(Received via mailing list)
either

> def update
>     @product = Product.find(params[:id])
>     if @product.update_attributes(params[:product])
>       flash[:notice] = 'Product was successfully updated.'
>>>> flash.keep and redirect_to :action => 'show', :id => @product and return
>     else
>       render :action => 'edit'
>     end
>   end

or

> def update
>     @product = Product.find(params[:id])
>     if @product.update_attributes(params[:product])
>       flash[:notice] = 'Product was successfully updated.'
>>>> show and render :action => 'show' and return
>     else
>       render :action => 'edit'
>     end
>   end

As said before, the second one may be considered better, because it
won't affect the client-side navigation. I haven't tested these, sorry
if there are some mistakes.
You also can use render component, which was created for this purpose,
yet somehow deprecated (performance?).
For the first solution, don't forget the flash.keep before redirect,
and the return after, which is not implied and may become mandatory if
you add things after the branch.
Wes G. (Guest)
on 2007-02-02 08:50
Benoit B. wrote:
> either
>
>> def update
>>     @product = Product.find(params[:id])
>>     if @product.update_attributes(params[:product])
>>       flash[:notice] = 'Product was successfully updated.'
>>>>> flash.keep and redirect_to :action => 'show', :id => @product and return
>>     else
>>       render :action => 'edit'
>>     end
>>   end
>
> or
>
>> def update
>>     @product = Product.find(params[:id])
>>     if @product.update_attributes(params[:product])
>>       flash[:notice] = 'Product was successfully updated.'
>>>>> show and render :action => 'show' and return
>>     else
>>       render :action => 'edit'
>>     end
>>   end
>
> As said before, the second one may be considered better, because it
> won't affect the client-side navigation.

Can you explain the statement above, in particular "it won't affect the
client-side navigation."?

Thanks,
Wes
Ben M. (Guest)
on 2007-02-02 11:36
(Received via mailing list)
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
Eric A. (Guest)
on 2007-02-02 16:26
(Received via mailing list)
Ben M. 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
Wes G. (Guest)
on 2007-02-02 17:19
I concur completely with Ben's comments above (which is why I asked for
clarification on some comments earlier).

I find it somewhat difficult to manage flash when I redirect regardless,
so I've done my best to use session variables that I am very careful to
clean up (when I know I'm at a point in the app. that I can).  Obviously
flash is session, but there's a lot less code if you use a before_filter
to pull stuff out of session and then just create/delete the session
variables appropriately.

For a couple of months now, I've been considering putting together a
session data management plugin which would allow you to define
"lifetimes" for session data easily, which in turn would hopefully help
make the "redirect all the time" scheme slightly easier to implement.

Thanks,
Wes
Chris O. (Guest)
on 2007-02-03 05:51
(Received via mailing list)
Thanks for the input.  That clears things up, although Ii was unaware
of issues flash session issues.  Would anyone care to expand on this?


On Feb 2, 8:19 am, Wes G. <removed_email_address@domain.invalid>
Wes G. (Guest)
on 2007-02-03 09:28
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.

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.

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.

Thanks,
Wes
George O. (Guest)
on 2007-02-03 11:11
(Received via mailing list)
On 2/3/07, Wes G. <removed_email_address@domain.invalid> wrote:
>
> For a couple of months now, I've been considering putting together a
> session data management plugin which would allow you to define
> "lifetimes" for session data easily, which in turn would hopefully help
> make the "redirect all the time" scheme slightly easier to implement.

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.)
George O. (Guest)
on 2007-02-03 11:14
(Received via mailing list)
On 2/3/07, Wes G. <removed_email_address@domain.invalid> wrote:
>
> 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.

> 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.
Ben M. (Guest)
on 2007-02-03 23:46
(Received via mailing list)
George O. wrote:
>       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
Wes G. (Guest)
on 2007-02-04 08:46
> 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.

I was saying sometimes I create an ActiveForm object (which is _like_ an
AR but doesn't descend from AR::Base).  Think of an AR object without a
backing table (so no DB interactions) and AR type validations...

The thing that I find troublesome about the flash is that if you use
AJAX calls in a page, for example, that will clear out your flash, and
you have to re-set needed objects on the flash.  I find that it's easier
to use a regular session object and clear it out when you _know_ you
can, and then you don't have to keep track of every possible request
combination that might cause the flash to be reset.  Does that make
sense?

Wes
George O. (Guest)
on 2007-02-04 17:02
(Received via mailing list)
On 2/4/07, Ben M. <removed_email_address@domain.invalid> wrote:
> >       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... ;-)

> 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 ]
George O. (Guest)
on 2007-02-04 17:06
(Received via mailing list)
On 2/4/07, Wes G. <removed_email_address@domain.invalid> wrote:
>
> The thing that I find troublesome about the flash is that if you use
> AJAX calls in a page, for example, that will clear out your flash, and
> you have to re-set needed objects on the flash.  I find that it's easier
> to use a regular session object and clear it out when you _know_ you
> can, and then you don't have to keep track of every possible request
> combination that might cause the flash to be reset.  Does that make
> sense?

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.
Ben M. (Guest)
on 2007-02-04 22:46
(Received via mailing list)
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
Ben M. (Guest)
on 2007-02-04 22:53
(Received via mailing list)
George O. wrote:
> On 2/4/07, Ben M. <removed_email_address@domain.invalid> 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? ;-)

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
Wes G. (Guest)
on 2007-02-05 07:20
Ben M. wrote:
> 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

Ben,

You certainly can.  I originally was going down this route, but I ran
into a lot of combinations of requests that I didn't foresee and I got
tired of peppering my actions with "flash[:blah] = 'blah'" and
"flash.keep(:blah)".

Instead, I opted for setting "session[:blah] = 'blah'" and then use a
before_filter on all appropriate actions (where the new blah object
hasn't been saved) called get_blah which is:

def get_blah
  @blah = session[:blah]
end

and then in some action when I am certain that I can get to blah via DB,
I set session[:blah] to nil.

If later, I insert some action that needs to get to unsaved blah, I just
add it to the before_filter list and I'm done.

It's basically the same thing, since of course, flash is just a special
area of the session.  But I'm going the route of setting on session so
that I don't have to directly manage all of the requests where that
flash variable would be to be set or kept.  And it's really only a valid
approach if I can reliably set and remove these unsaved objects on
to/off of the session.

This is not to say that I _don't_ use flash - I definitely do, when I am
in control of the redirect and I am sure that I only need what I put on
the flash for one more request.

Wes
Quasor (Guest)
on 2007-02-05 07:58
Wes G. wrote:
> I concur completely with Ben's comments above (which is why I asked for
> clarification on some comments earlier).
>
> I find it somewhat difficult to manage flash when I redirect regardless,
> Wes

word!
This topic is locked and can not be replied to.