What does redirect_to #client mean? - ruby-on-rails

Here I have a scaffold for clients, and in the create method in ClientsController,
if #client.save
redirect_to #client
else
render :action => "new"
end
Here what it means to redirect to an instance variable of Client class?
Also, in else, render renders the view for new if save fails. However, how does the controller keep the original input at the same place? (For example I fill the form and send it but fail to proceed, so it takes me back to the new client page with my original input at the right place.)

redirect_to #client redirects to the clients/show/1 directory. where 1 is the id of client.
and render :action => "new" render the new action, for more detail see http://guides.rubyonrails.org/layouts_and_rendering.html

Related

Passing an additional variable in respond_to method after create controller is successful

I'd like to pass the view controller a params that it can consume after a successful create event has been triggered. Right now at the end of the create event I have the following:
respond_to do |format|
if #link.save
format.html { redirect_to #link, :notice => 'Link was successfully created.', :first => 'true' }
else
format.html { render action: "new" }
end
end
I was trying to pass a 'first' parameter in the 3rd line down, but I'm either not calling it correctly on the view page or I'm not setting it correctly here. Below is the code I'm using to try and call it on the view page:
params[:first]
I think Quatz's answer won't work. Instance variables will not be available after you send redirect_to, which is because redirect_to actually returns 302 back to the browser with a redirect Location header so that the browser will be redirected. So the instance created in previous request won't be available at all.
There could be two solutions to your problem:
Use session. session[:first] = true before you call the redirect_to. After that, you can access the value using session[:first]
Modify your redirect_to to something like
redirect_to link_path(#link, :first => 'true'), :notice => 'Link was successfully created.'}
using the named routes you can pass in parameters.
You can use instance variable in such case. Create an instance variable #first like this.
#first = params[:first]
In your view file use this.
<%= #first.attributes %>

On create error, should I render `new` or redirect to `new`?

Suppose I have something like this:
def new
#user = User.new
end
def create
#user = User.create(params[:user])
if #user.save
flash[:notice] = 'User created'
redirect_to :action => 'list'
else
flash[:error] = 'Some error here!'
render 'new'
end
end
I think the code is clear.
The problem here is, when the #user object is not saved successfully, should I render new (as above) or should redirect to new?
I know if redirect to new the data input by the user is lost, but if I render new, the URL will be /users/create instead of /users/new (which is ugly!).
You are correct in not using redirect. Redirect is loading an entirely new resource.
render however will keep your session data fresh, and depending on how your form is set up, should repopulate whatever data was inputted.
You mention:
I know if redirect to new the data input by the user is lost, but if I render new, the URL will be /users/create instead of /users/new (which is ugly!).
No, this is not true. If you say render 'new', it will go to the url users/new not create. Create as an action only handles POST requests to your controller, and generally never has a view associated with it. It will instead refer to the new action to handle any errors and displaying of forms.
The create action has this in common with the update action which does the same thing by handling only PUT requests, but refers to the edit action to handle the displaying of views.

Are redirect_to and render exchangeable?

For the code below, what happens if replacing redirect_to with render or vise verse?
def create
#product = Product.new(params[:product])
respond_to do |format|
if #product.save
format.html { redirect_to(#product, :notice => 'Product was successfully created.') }
else
format.html { render :action => "new" }
end
end
end
It seems OK replacing one with the other in code above. Is there a place where only redirect_to or render has to be used? Render does nothing but rendering a view. Redirect_to sends 302 request to server and current parameters are lost after redirecting.
Thanks.
If you're using render, when the user refreshes the page, it will submit the previous POST request again. This may cause undesired results like duplicate purchase and others.
But if you're using redirect_to, when the user refreshes the page, it will just request that same page again. This is also known as the Post/Redirect/Get (PRG) pattern.
So the place where redirect_to should be used is when you're doing a HTTP POST request and you don't want the user to resubmit the request when it's done (which may cause duplicate items and other problems).
In Rails, when a model fails to be saved, render is used to redisplay the form with the same entries that was filled previously. This is simpler because if you use redirect, you'll have to pass the form entries either using parameters or session. The side effect is that if you refresh the browser, it will try to resubmit the previous form entries. This is acceptable because it will probably fail the same way, or if it's successful now, it was what the user should expect in the first place anyway.
For more in depth explanation about render and redirect, you should read this article.
When you redirect you will generate a new request that hits a controller method, render just renders the associated view. You use render in the create because you want to keep the state of the model object if the save fails so that you can render info about its errors. If you tried to redirect to the new_product path you would create a new model object and loose all the form data the user entered and any errors etc etc
EDIT (with some more info):
An example of a situation where you MUST use redirect_to is if your view template uses instance variables that are not initialized in the controller method you are redirecting from. So you probably could not call render {:action => 'index'} in your create method because the index template probably makes use of a #products variable but your only initialized #product so it would cause an exception
Here is a complete list of what the two methods do that I follow:
1) redirect_to will issue an HTTP 302 status code by default. A 302 redirect is a temporary change and redirects users and search engines to the desired page for a limited amount of time until it is removed. You can optionally specifiy a 301 status code to redirect_to. A 301 status code is used when any page has been permanently moved to another location. Users will now see the new page as it has replaced the old page. This will change the URL of the page when it shows in search engine results.
2) redirect_to will issue a new HTTP request, since it is redirects to a different controller action or URL. You should not make the browser need to make a fresh call unless you really have to, so always question when you are using redirect_to and if it is the right thing, or perhaps a render would be better.
- redirect_to will cause any automatic template rendering of the current action to be skipped.
3) render will issue an HTTP 200 status code by default ( but with an invalid ActiveRecord object, you may want to change this to 422 unprocessable entity). The HTTP 200 OK success status response code indicates that the request has succeeded. The 422 (Unprocessable Entity) status code means the server understands the content type of the request entity nd the syntax of the request entity is correct but was unable to process the contained instructions.
4) render will render a template and any instance variables defined in the controller action will be available in the template. Of course, instance variables will not be available if the subsequent action that redirect_to invokes. IMPORTANT POINT: Redirect hits the controller while Render does not, so if you render a different template, it will not hit the action associated with that template and so those instance variables will not be available!
5) With render, use flash.now, instead of the normal flash.
flash.now[:error] = "There was a problem"
# not
flash[:error] = "There was a problem"
6) If you don't, then the flash message may not show up on the page that's rendered, and it will show up on the next page that's visited.
7) render will not cause the current action to stop executing! redirect_to will not cause the current action to stop executing! You need to invoke 'return' if you need to bypass further execution of code in the action! In the below code, there is an explicit render at the bottom and so you must do a return to avoid an error of redirect and render both being present:
def update
#record = Record.new(record_params)
if #record.save
flash[:success] = "record was successfully saved"
redirect_to records_path
return
end
flash.now[:error] = "please fix the problems in the record"
render :edit
end
Another option:
def update
#record = Record.new(record_params)
if #record.save
flash[:success] = "record was successfully saved"
redirect_to records_path
else
flash.now[:error] = "please fix the problems in the record"
render :edit
end
end
8) The flash message provides a way to pass temporary primitive-types (String, Array, Hash) between actions. Anything you place in the flash will be exposed to the very next action and then cleared out. This is a great way of doing notices and alerts:
class PostsController < ActionController::Base
def create
# save post
flash[:notice] = "Post successfully created"
redirect_to #post
end
def show
# doesn't need to assign the flash notice to the template, that's done automatically
end
end
show.html.erb
<% if flash[:notice] %>
<div class="notice"><%= flash[:notice] %></div>
<% end %>
Since you can have both notices and alerts in the flash, you can display both notices and alerts this way:
<% flash.each do |key, value| %>
<%= content_tag :div, value, class: "flash #{key}" %>
<% end %>

Rails validation conditional redirect

I am currently having an issue with how Rails is performing and responding to a validation result. I have a user registration form. The user could hit this form in two different places. They could hit the form from the homepage or from users/new. Both forms will post to the same place as I am trying to keep it DRY.
The users/new page works as is expected. If the user has a validation issue it will return and populate the form. Where I get a problem is on the home page. If a user has a validation issue it now redirects to the users/new page. I would much prefer that when on the home page I would return the user to that same page and show the validation results there. Is there a way in the controller to redirect to the form the user was at?
def create
#user = User.new(params[:user])
respond_to do |format|
if #user.save
format.html { redirect_to(#user, :notice => 'User was successfully created.') }
format.xml { render :xml => #user, :status => :created, :location => #user }
else
format.html { render :action => "new" } # I'm thinking I can do something here?
format.xml { render :xml => #user.errors, :status => :unprocessable_entity }
end
end
end
I have tried to change the render :action => 'new' line to redirect to the user url but it hasn't worked. Is there something I'm missing?
First, I would add querystring parameters to the URL it is posting to with the controller and action that it came from with something like this:
# Using form_tag
<%= form_tag user_path(#user, :controller_name => controller.controller_name, :action_name => controller.action_name) do %>
# Using form_for
<%= form_for #user, :url => user_path(#user, :controller_name => controller.controller_name, :action_name => controller.action_name) do %>
Then, you can update that line in the create action of your controller like this:
render '#{params[:controller_name]}/#{params[:action_name]}'
Update
I just realized that using the code above, will render the correct view the first time validation fails, but if validation fails a second time, it will try to render the users/create view. If this is the route you want to take, you should not use controller.controller_name, etc in the view, but assign #controller_name correctly and use that variable instead. However, this only adds to the 'overkill' comment made by Xavier.
Art's on the right track, but you can't use a redirect, as you need the instance variable #user that's set in your controller, which'll be lost on a new HTTP request (because ever request is handled by a new, clean controller instance).
But you can use the referer information yourself, and use that to pick the right page to render:
render :action => (request.referer =~ /\/users\/new/)? :new : :index
Note: Another answer popped up while I was posting that suggests adding the old controller / action fields to your form, but that seems like overkill to me - you already have all the information you need in request.referer.
Hope that helps!
Try redirect_to :back
It's a shorthand for redirect_to(request.env["HTTP_REFERER"])
oops, it only works for success. sorry
well, then you have to check inside the block (after format.html) where he came from (by looking at request.env["HTTP_REFERER"]) and render respective action.

Ruby on Rails when create method fails, render loses local variables

Hey guys I have a simple create method with some validations and whenever the create method fails due to validation errors it re-renders the 'new' action.
The problem is in my new action/view I have a local variable that is established in the action and passed to a partial to render some related information to what the user is creating.
Now when my create action fails and I try to re-render the 'new' action I'm getting the always awesome
undefined method `cover' for nil:NilClass
error.
What is the best way to handle re-establishing my action's local variables on a render instead of redirecting to the action again and the user losing the data they input?
For some clarification. Here is some sample code:
#controller.rb
def new
#summary = User.find(params[:user_id])
#page = Page.new
end
def create
#page = Page.new(params[:page])
if #page.save
redirect_to #page
else
render :action => 'new'
end
end
in my new.html.erb file i have something like this
<%= #summary.cover %>
#page form etc...
When you create the object and attempt to save it, the object still holds the values and the validation errors, so pass it on into the render. Usually it is named the same in your create method as it is in your new method, so the template just works.
if #my_object.save
flash[:notice] = "Successfully created."
redirect_to ....
else
render :action => 'new' #assuming new.html.erb uses #my_object
end

Resources