Rails: create from two pages - ruby-on-rails

I'm very new to Rails, so maybe I'm just missing the "Rails way" of doing this, but I have a model, call it Post. I can create a post from the canonical posts/new page but also from another page, say home/index.
In home/index i have a form_for #post (slightly different from the one in posts/new, but say that i can use a partial). The problem is that in the PostController.create I cannot pass the newly created #post object back to home/index (in case of errors) because:
if I don't specify a page to render, it will automatically render posts/new
i don't know the calling page in order to redirect it to the right calling page (posts/new or home/index)
even if i knew it (hacking the request referrer or using redirect_to :back), redirect_to doesn't pass objects back, so that #post is empty when called from home/index
Any help? thanks
EDIT
Maybe a possible solution would be to get the calling controller / action from the request and render it back. Any way to do this?

In theory, you could achieve what you're trying to do by checking the referer:
def create
#post = Post.new
if #post.update_attributes(params[:post])
# redirect as appropriate
else
render :action => case request.referer
when new_post_path then "posts/new"
when "/" then "home/index" # assuming that home/index is the root of the site
end
end
end

To get the referrer page, you can make a hidden field with the name redirect. You can use it in the controller.
redirect_to params[:redirect] || posts_path
Have you tried that you pass the post's id in the query string to the home/index
eg: /home/index?post_id=42

You can find out who called your page by looking at
request.referrer
I don't know if this is the "rails way" but here's my solution.
You can add a route for
match home/index/(:id) => "home#index"
and redirect to this after creating the Post. Then in your Home controllers index action just do a
#Post = Post.find(params[:index]) if params[:index]
Your view should display the post if #Post exists
I like this approach because it keeps all the logic where it should be. Routing logic in the controller and view logic in the views.

Related

Rails form action and routes

I have a controller action that requires parameters, and has to be accessed both through get and post.
with get it looks like:
get 'cart/add:id,:qty' => 'cart#add', as: :addToCart
the controller action is:
def add
product = Product.find(params[:id])
if ::AddToCart.new(#cart, product, params[:qty]).execute
flash[:succes] = "Product(s) added."
else
flash[:failure] = "Product cannot be added."
end
redirect_to request.referer
end
how should the post route look like if i would like it to use the same action.
PS: i use the get version as a link_to when only one product is added, the post route is needed because the quantity will be unknown
You should make this strictly a post route. If you want, you can default the quantity to one when it isn't provided. Or, use ajax to post to the route with a quantity of one when they click the "add one" link. That way it can look and seem to behave like a regular link_to sort of link, but you'll still have the benefits of only updating data via POST and leaving all your actual GET requests idempotent.

redirect_to # syntax meaning

What does the code
redirect_to #user
when used in a controller method, mean in Rails?
Where exactly does it redirect to? Does it redirect to a Users controller (If so, which method of the controller), or does it not go through the controller and instead go directly to a view?
Basically it looks up a bunch of stuff about how your resource routes work
think of it like this
send("#{#user.class.name.underscore.downcase}_path", #user)
this is probably not the exact code but it should help you visualize whats actually going on.
The controller always runs, in this case it would be the show action of your users controller unless you have some funky router options.
rake routes explains how the routes are laid out in this case show is
get /users/:id => users#show :as => "user_path"
the substitution from your incoming model works like this
a regex is created from your route
:id being a param
would match to inbound_object to the path function which is #user
:id is replaced with #user.id
From the redirect_to docs:
Redirects the browser to the target specified in options. This parameter can take one of three forms:
...
Record - The URL will be generated by calling url_for with the options, which will reference a named URL for that record.
And from the url_for docs:
Passing a record (like an Active Record or Active Resource) instead of a Hash as the options parameter will trigger the named
route for that record. The lookup will happen on the name of the
class. So passing a Workshop object will attempt to use the
workshop_path route. If you have a nested route, such as
admin_workshop_path you’ll have to call that explicitly (it’s
impossible for url_for to guess that route).
This is the equivalent of writing
redirect_to user_path(#user)
More Rails magic at work here, for better or worse.
it calls the show function of users like this
redirect_to user_path(#user)
important point : if we see the routes then :id is passed but here object is getting passed
which get converts into the id of the user by
def to_param
self.id
end
which is available in model and we can overwrite it to
so basically to conversion take place first
redirect_to #user => redirect_to user_path(#user)
then
#user => #user.id

Rails : Is it possible to put a "create" form on a "index" or "show" view?

Hi I'm trying to work on my first rails project and was wondering if it was possible to combine the show or index view (of photo albums) with a form that creates more photo albums. Would the URL not have the correct parameters to do this? Would I be able to set the action on the form to make it "create" and redirect_to the index page again on success?
Sure you can do this, just put the form code on whatever page you want to create/update from and it will work.
The only issue is where the action would redirect you to after a successful or unsuccessful create/update: usually from the new page, you would redirect to the newly-created record (show action for the new record) on success and back to the new action on failure (with the errors on the form fields). If you want to create/update records from different pages, and have the action redirect to different pages in each case, then you'll have to do just a bit more work.
On possibility would be to add a hidden parameter to the form with the action to redirect to, and make the action check for it and redirect accordingly. For example:
VALID_REDIRECT_ACTIONS = ["show", "index"]
def create
...
if #photo.save
flash[:success] = "Photo successfully created!"
if VALID_REDIRECT_ACTIONS.include?(params[:redirect])
redirect_to params[:redirect]
else
redirect_to #photo
end
else
...
end
end

where does the url routes get resolved when you call render in rails controller actions

Is there any method that i should look at in rails3.2 source code so as to know where the navigation or the url part of the render call get resolved?
The reason is, i have a small app in which url is of the form
www.example.com/bob/edit
the above route as it suggests renders the edit form.EDIT: i was able to get to this route by modifying response on the link_to helper.
def update
#when validation passes
redirect_to #user
#when validation fails
respond_to do |format|
format.html {render :action => "edit"}
end
end
Now the problem is when a validation error occurs on submission to update action of users_controller,
the url becomes
www.example.com/users/bob/edit
config/routes.rb
get "users/new", to: => "users#new"
resources :users
as you can see there's nothing interesting happening in routes,
in models/user.rb
def to_param
"#{name}"
end
in views/edit.html.erb
form_for(#user) do |f|
end
Observation: here when the form is rendered afresh, form 'action' points to "users/bob" but when the form is re-rendered 'cos of validation error, form action mysteriosly changes to "users/" which is weired and if i remove the to_param in user.rb model it works fine
Though its not such a big deal, i was thinking where, if i needed to override the url that is generated on render call, to change?????
Any suggestions and pointers to explore are wecome....
I'm not sure how you're getting the URLs you're getting, but a general answer to your question would be it doesn't. The URL you see after sending a request is the URL the request was sent to (or redirected to), not that of the page you came from, nor that of the template you render in the end. In your case, I'm guessing the problem is that you created a custom URL for the edit page, but not for update, and your form_for(#user) is sending the request to your update URL (probably PUT "/users/bob").
To fix this, the first thing is to create your custom update route. Maybe something like:
put ":id/update", to: => "users#update"
And then have your form_for use that URL:
form_for(#user, :url => "#{#user.to_param}/update")

Forward the user back to a post after adding a comment

This is probably a fairly simple thing to do - I have a site with a news feed that shows various posts. There are also other pages that similarly show posts. Comments can be added to these posts directly from any of these pages.
What I need to do is redirect the user back to the URL they came from, once they've added a comment, and to the particular post they commented on (each post has an id of post-#{post.id}, so I'd just have to stick #post-2 or whatever to the URL.
If the post could not be saved, for whatever reason, I'd also like to have the content that the user had submitted pre-filled into the comment box after the redirect.
How can I do these two things? The first is the more important one..
I'd have to store the URL that the user is coming from in the session on the new action and redirect to this on the create action? How can I get the URL?
I'm on Rails 3.1 by the way.
Thanks for the help!
You could use redirect :back which will send the user back to the page that issued the request
redirect_to :back
Assuming Comment is a resource nested under Post in your routing (/posts/123/comment/new):
def create
comment = Comment.new(params[:comment])
if comment.save
redirect_to post_path(comment.post)
else
redirect_to new_post_comment_path(params)
# or maybe you have the comment form on the Post#show page
# redirect_to post_path(params)
end
end

Resources