Rendering an action in another controller - ruby-on-rails

(Rails 2.3.5)
I have two scaffolds: Directories & Users
For a Directory show action (say Show action: "\directories\2"), I took the User\New form and made it a partial so the user can add users to the Directory. What I can't figure out is how in the create action I can return to "\directories\2\show" if there are any validation errors. Returning if the User.save is successful works fine, I just can't figure out how to format a Render action to return to the directory and display error messages and fields in the New User partial.
This works fine if a save is successful, using the same thing if there is an error will work except error_messages will not be displayed (I know that error messages are only suppose to be passed on a Render, not a redirect, but I can not figure out the syntax involved for a render action when an id parameter is involved):
format.html { redirect_to directory_path(#user.directory_id) }
Users Create Action called by partial in Direcory Show action:
def create
#user = User.new(params[:user])
respond_to do |format|
if #user.save
flash[:notice] = 'User ' + #user.name+ ' was successfully created.'
format.html { redirect_to directory_path(#user.directory_id) }
format.xml { render :xml => #user, :status => :created, :location => #user }
else
# what to do here to successfully return to 'directories\show\(#user.directory_id)'
# and what to do here that successfully passed the error_messages
end
end
end
Thanks for any help - hopefully that makes sense

To render an action from another controller you need to specify the template you want to render.
render :template => 'other_controller/view_template_name'
P.S: Keep in mind that you'll have to define any instance variables that the other controller action defines which are necessary for the view to render because rendering a template will not call the other controller's function before rendering the view.

Related

Rails redirect if validation fails

In a Rails 3.2 app, I have a validation for an attachment type.
Attachment model:
class Attachment < ActiveRecord::Base
validates_presence_of :name
validates_attachment_presence :attach, :message => "No file selected"
validate :check_type
def check_type
if self.costproject_id != nil
if self.attach_content_type != 'application/pdf'
self.errors.add(:pdf, " ONLY")
return false
end
end
end
But, the return false sends me to this URL:
http://localhost:3000/attachments
I want it to go back to the previous input screen:
http://localhost:3000/attachments/new?costproject_id=2
How do I accomplish that?
Thanks!!
UPDATE1
Perhaps the redirect has to take place in the controller?
format.html { render action: "new" }
Attachment controller:
# POST /attachments
# POST /attachments.json
def create
#attachment = Attachment.new(params[:attachment])
respond_to do |format|
if #attachment.save
format.html { redirect_to session.delete(:return_to), notice: 'Attachment was successfully created.' }
format.json { render json: #attachment, status: :created, location: #attachment }
else
format.html { render action: "new" }
format.json { render json: #attachment.errors, status: :unprocessable_entity }
end
end
end
I changed this line:
format.html { render action: "new" }
To:
format.html { redirect_to request.referer }
And now it goes back to where I want. But, I've lost the errors - they don't display.
To help you understand what's going on here. When you go to /attachments/new you are rendering a form. When you press submit, you are sending a POST request to /attachments, which invokes the create action.
You're create action appears to be solid and idomatic. However when you render action: "new" in the case of an error, it's not a full redirect, it's rendering the form in the context of the current action.
Normally this is fine, because idomatic rails would have you building a single, very similar, model object in both new and create, and the form for helper would render that object. However your new action is creating all kinds of objects based on a large assortment of query parameters, which I'm guessing is why you are seeing behavior you don't like.
I expect your final solution will involve bringing all those parameters into Attachment in some way, if they don't need to be saved to the database, you can make attr_accessors on Attachment
# Model
class Attachment < ActiveRecord::Base
attr_accessor :worequest_id, :workorder_id # etc
end
# View
<%= form_for #attachment do |f| %>
<%= f.hidden :worequest_id %>
<% end %>
Approaching it this way, your post request params will look like
{
attachment:
{
worequest_id: 1,
# etc
}
}
And you would also need to rework your query params to nest the inidividual ids inside of an attachment
/attachments/new?[attachment][worequest_id]=1
This way you could build attachment from params in both actions:
Attachment.new(params[:attachment])
And now your current create action should more or less work as expected, because now it's idomatic rails.
You still aren't going to get the new action with the same query params, but since you are taking those params and filling them in hidden fields on the form, they won't be lost when you try and fail to create. In any case, unless you do something to persist the values between requests, the POST to /attachments is going to wipe out the ery params.
Try this.
Replace
return false
With
redirect_to request.referrer || root_url
Note: root_url here is a catchall. Also this is Rails 4, I do not know if it also applies to Rails 3. Worth a try, though.
Debug ideas
First confirm a simple redirect_to root_url (or whatever name you use for your root) works in your controller
redirect_to root_url
Then, once redirect_to confirmed working, focus on getting the REST interface "request." information. There's a Rails 3 discussion here which may help you.
How to get request referer path?

Conditional redirect after update

I would like to do a conditional update in ruby on rails 3.1
Where based on the location you came from, after update, an redirect will be done.
Splitted my 1 big form in to separate smaller ones, so now the Idea is to redirect to the correct subform.
For example the form can be submitted from:
profile basics form
Profile details form
The only thing I could come up with is checking the action name and use that to redirect. But its very ugly and long code and not fully working either. What would be the railsway of doing this?
This is my controller update action:
def update
#profile = Profile.find(params[:id])
respond_to do |format|
if #profile.update_attributes(params[:profile])
format.html { redirect_to #profile, notice: 'Profile was successfully updated.' }
else
format.html {
render :action => "edit_basics"
#
}
end
end
end
Why not just pass the redirect location as a hidden_field in the form, then have each form set it as needed:
redirect_to params[:redirect_location]
You could also do this using steps or something if you don't want to expose the raw string in your HTML:
redirect_to location_for_step(params[:step])

What is the purpose of the new controller action in Rails?

A scaffold generates the new action like this:
def new
#product = Product.new
respond_to do |format|
format.html # new.html.erb
format.xml { render :xml => #product }
end
end
def create
#product = Product.new(params[:product])
respond_to do |format|
if #product.save
format.html { redirect_to(#product, :notice => 'Product was successfully created.') }
format.xml { render :xml => #product, :status => :created, :location => #product }
else
format.html { render :action => "new" }
format.xml { render :xml => #product.errors, :status => :unprocessable_entity }
end
end
end
and the view renders a partial, named form. Since the new form renders with the action set to create a new product, what is the purpose of #product? I see that the create action instantiates a new object as well. Is it only used so that you can tie the form to an object, so that everything goes from action to action correctly?
You can think of #product in the new action as being an unsaved object that simply fills out the form fields that are rendered in the view. This makes new.html.erb pretty much the same as edit.html.erb and allows them to share a single partial, _form.html.erb.
When this partial is used in the new action, the fields are populated by a fresh, empty, and unsaved #product object. This is the Product.new that appears in the new action. When the partial is used in the edit action, you've got a #product object that presumably has values for all its attributes. Now, suppose you didn't use #product in the new action. The form used in new.html.erb would need to be different than the form used in edit. Good luck maintaining them if you ever add a new field to the model.
Another advantage of this approach is that you can pre-populate attributes of the new #product before they're rendered in the view. Suppose you want to use the name "new product" as the default name for each product. You can do this in the new action this way:
def new
#product = Product.new(:name => 'new product')
respond_to do |format|
format.html # new.html.erb
format.xml { render :xml => #product }
end
end
One main purpose is so that you can use the same form for new and edit.
The controller passes the #product object (new or existing) and Rails sees whether it's a new record or an existing record. It makes certain decisions based on that such as to pull the record values into the input fields (existing) and which controller action to send the form on submission.
If you have a form_for, the new action is used to properly initialize the #product in form_for #product, which expects an ActiveRecord model. If I remember correctly, the Product's scope (for any controller action) ends with the request, so the create action has no knowledge of the new action, requiring another Product to be initialized.
The form_for method uses the #product variable to correctly assign the form to the resource controller to find the correct URL, id (in the case of an update), and more. You can read about it in section 2.3 of http://guides.rubyonrails.org/form_helpers.html
If you're that concerned about memory usage, you don't have to initialize #product, but then you would have to manually create your own form without using the nice resource-based form_for.

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.

Rails controller - redirect to website after create?

I am working on a basic app that has a public facing form (for enquiries) that anyone can fill in and submit. The results are then stored for the company to do what they want with.
I have made sure that if the user is not logged in they can only access the create page, but once they submit the form, as expected, they are taken to the login page because its trying to show them the show page.
My current controller is as follows:
# POST /enquiries
# POST /enquiries.xml
def create
#enquiry = Enquiry.new(params[:enquiry])
respond_to do |format|
if #enquiry.save
format.html { redirect_to(#enquiry, :notice => 'Enquiry was successfully created.') }
format.xml { render :xml => #enquiry, :status => :created, :location => #enquiry }
else
format.html { render :action => "new" }
format.xml { render :xml => #enquiry.errors, :status => :unprocessable_entity }
end
end
end
I would imagine it's this line that needs to change:
format.html { redirect_to(#enquiry, :notice => 'Enquiry was successfully created.') }
Is it possible to do:
format.html { redirect_to(http://www.google.com) }
Yes, you can certainly redirect_to("http://any.url.com/you/want") from your controller or do whatever else you want if create is successful. Redirecting to the show action is just a common pattern.
Is this really what you want to do, though? If you redirect your user to an outside website after submitting the form, you can't give them any feedback at all about what just happened. The user might try to submit the Enquiry again, or worse, they might think something went wrong and just forget about it, lose interest, etc. I'd strongly recommend creating a "Thanks for your enquiry!" page and redirecting anonymous users there.

Resources