I'm setting up friendships within a RoR website. The model for it is user_id, friend_id, and pending (boolean). I followed the RailsCast on friendships for the most part, but making some changes to it too. What I have so far is that when you go to a user's page you can click Request Friendship and the code uses:
user_friendships_path(current_user, :friend_id => #user), :method => :post
This calls the create method in the Friendships controller. It automatically sets pending to true. What I want now is to have a link to Accept it which would just turn pending to false. So I am trying to set it up like
(<%= link_to "Accept", user_friendship_path(:user_id => current_user.id, :friend_id => friendship.user.id, :pending => 'false'), :method => :put %>)
I don't actually want to go to the edit page, because it just needs to set that boolean to false, so I want to call the update directly. But when I run this page, I get the error:
No route matches {:action=>"destroy", :controller=>"friendships", :user_id=>1, :friend_id=>2, :pending=>"false"}
I don't understand why. I'm not calling the destroy (that would be with :method => :delete), and there actually is a destroy method within the Friendship controller.
The resources are set up like:
resources :users do
resources :friendships
end
And the paths from running "rake routes" are:
user_friendships GET /users/:user_id/friendships(.:format) {:action=>"index", :controller=>"friendships"}
user_friendships POST /users/:user_id/friendships(.:format) {:action=>"create", :controller=>"friendships"}
new_user_friendship GET /users/:user_id/friendships/new(.:format) {:action=>"new", :controller=>"friendships"}
edit_user_friendship GET /users/:user_id/friendships/:id/edit(.:format) {:action=>"edit", :controller=>"friendships"}
user_friendship GET /users/:user_id/friendships/:id(.:format) {:action=>"show", :controller=>"friendships"}
user_friendship PUT /users/:user_id/friendships/:id(.:format) {:action=>"update", :controller=>"friendships"}
user_friendship DELETE /users/:user_id/friendships/:id(.:format) {:action=>"destroy", :controller=>"friendships"}
Any help would be greatly appreciated. Please let me know if you require more information.
Thanks.
The path that exists according to rake routes is
user_friendship PUT /users/:user_id/friendships/:id(.:format) {:action=>"update", :controller=>"friendships"}
The method you are using is put, but you aren't supplying ':id'.
There are two solutions depending on what you are trying to do:
Change the method to get - that will point you to the new action
Add :id to your URL builder - that will point you to the update action
Your code is leaning towards the second, but I think if you want to create a new friendship, the first would be better.
I'm baffled as to why the URL helper is mapping to a destroy action. However there is a simpler problem: user_friendship_path expects params :user_id and :id, not :friend_id.
Related
I'm struggling with this line in the _form: <%= simple_form_for(#post, url: blog_path) do |f| %>, which gives me the error:
No route matches {:action=>"show", :controller=>"posts"} missing required keys: [:id]
Keep in mind that in my routes I have: resources :blog, controller: 'posts', which is to say I am working off of a posts MVC, but I wanted /posts/ to be replaced by /blog/ in the routes.
posts_controller
def new
#post = Post.new
end
def edit
end
The _form works when I go to edit, but not create new.
routes
blog_index GET /blog(.:format) posts#index
POST /blog(.:format) posts#create
new_blog GET /blog/new(.:format) posts#new
edit_blog GET /blog/:id/edit(.:format) posts#edit
blog GET /blog/:id(.:format) posts#show
PATCH /blog/:id(.:format) posts#update
PUT /blog/:id(.:format) posts#update
DELETE /blog/:id(.:format) posts#destroy
model_path by default routing logic in Rails leads to blog#show => /blogs/:id
Change it to blogs_path.
Looking at you routes, I see obvious naming conflict, you must be defined routes wrong.
Be sure it looks like resources :posts, :as=>"blogs", both plural.
UPD
If you want to have only one blog, then resource :post, :as=>"blog", both singular.
But that means one actual input. I'm quite sure you speak of blog/post1, blog/post2, otherwise I can't see any sense in calling it blog?
sorry my english...
<%= simple_form_for(#post, url: blog_path) do |f| %> In this line you are redirecting the form to show (blog_path)
But , depending on the route , the show needs an id (blog GET /blog/:id(.:format)).
You must create a "create" method in the controller that receives the parameters strong ...
There is another method you can use: rails generate scaffold_controller posts
This will build the full CRUD , so you only have to configure the parameters
I hope that helps you
I got best of both worlds this way:
resources :blog, to: 'posts'
resources :posts
I'm working on assignment 2 problem 3 for saas class. I'm totally a rails beginner, and having trouble with the problem.
The assignment asks you to make column name "movie title" a link, which sort the movies by name
What I'm doing is make "movie title" a link:
%th#title_header= link_to 'Movie Title', :controller => 'movies', :action => 'sort_by_title'
Add a custom action to movies_controller:
def sort_by_title
#movies = Movie.find(:all, :order => "title")
render movies_path
end
Then rails give me error:
No route matches {:controller=>"movies", :action=>"sort_by_title"}
Then I say ok and add it to the route file:
match '/movies?sort_by_title', :to => 'movies#sort_by_title'
Now the index page renders fine but nothing happens when click the movie_title link.
Am I on the right path or totally wrong?
The rake routes prints:
movies GET /movies(.:format) {:action=>"index", :controller=>"movies"}
POST /movies(.:format) {:action=>"create", :controller=>"movies"}
new_movie GET /movies/new(.:format) {:action=>"new", :controller=>"movies"}
edit_movie GET /movies/:id/edit(.:format) {:action=>"edit", :controller=>"movies"}
movie GET /movies/:id(.:format) {:action=>"show", :controller=>"movies"}
PUT /movies/:id(.:format) {:action=>"update", :controller=>"movies"}
DELETE /movies/:id(.:format) {:action=>"destroy", :controller=>"movies"}
/movies?sort_by_title(.:format) {:controller=>"movies", :action=>"sort_by_title"}
Thank you
Why create a route for this when it's really just a GET parameter? It's not a new action either, it's still displaying all movies, just in a different manner.
Modify your index action to do something like this:
def index
#movies = Movie.scoped
#movies = #movies.order('title') if params['sort'] == 'title'
end
and then update your link:
link_to 'Movie Title', movies_path(:sort => 'title')
Resist the temptation to do just Movie.order(params['sort']) if params['sort'], as that opens up a potential SQL injection and, if not, just allows an attacker to discover all the columns in your table. It also has the potential to fail if the user just inputs a bad sort value in the URL.
Should it like this:
match '/movies/sort_by_title', :to => 'movies#sort_by_title'
rails 3.1
rake routes for admin/sections_contoller
trigger_admin_section GET /admin/sections/:id/trigger(.:format) {:action=>"trigger", :controller=>"admin/sections"}
admin_sections GET /admin/sections(.:format) {:action=>"index", :controller=>"admin/sections"}
POST /admin/sections(.:format) {:action=>"create", :controller=>"admin/sections"}
new_admin_section GET /admin/sections/new(.:format) {:action=>"new", :controller=>"admin/sections"}
edit_admin_section GET /admin/sections/:id/edit(.:format) {:action=>"edit", :controller=>"admin/sections"}
admin_section GET /admin/sections/:id(.:format) {:action=>"show", :controller=>"admin/sections"}
PUT /admin/sections/:id(.:format) {:action=>"update", :controller=>"admin/sections"}
DELETE /admin/sections/:id(.:format) {:action=>"destroy", :controller=>"admin/sections"}
routes.rb
namespace :admin do
resources :sections do
resources :items
resources :parameters
get :trigger, :on => :member
end
...
end
view
<%= link_to "Add a section", new_admin_section_path, :class=>'add-btn' %>
generated link
http://localhost:3000/admin/sections/new
result
No route matches {:action=>"show", :controller=>"admin/sections",
:id=>#<Section id: nil, ..., meta_description: nil}
strange bug or my mistake. other controllers has similar routes and all works fine.
for ex.:
<%= link_to 'Add a group', new_admin_group_path, :class=>'add-btn' %>
works GREAT!
please, help or i'll kill myself someday
upd1 same problem on heroku with this app.
upd2 join github issue: https://github.com/rails/rails/issues/4704
i found the answer.
once i put this string in view 'admin/section/_form'
<%= link_to 'delete', admin_section_path(#section), :method => :delete, :confirm => "Sure?" %>
i used this form for creating and editing. so combination of new object and deleting link for it caused the bug.
i used debugger for analysis.
Look at your "create" method in sections_controller.
I guess your section is correctly created but it redirect to "show" action. And "show" view may not exist.
Have you checked your database ? Is the section saved ?
I'm getting into Rails and trying to add a "vote" feature on a blog setup from here: http://guides.rubyonrails.org/getting_started.html
In app/controllers/posts_controller.rb I created this:
def incvotes
#post = Post.find(params[:id])
post.update_attributes(:upvotes => 1 )
format.html { redirect_to(#post, :notice => 'Vote counted.') }
format.xml { head :ok }
end
In app/views/posts/index.html.erb I created this:
<%= link_to 'Vote', :controller => "posts", :action => "incvotes", :id => post.id %>
But the link is giving the error
No route matches {:controller=>"posts", :action=>"incvotes", :id=>1}
I'm missing something here, but not sure what.
rake routes:
incvotes_post POST /posts/:id/incvotes(.:format) {:action=>"incvotes", :controller=>"posts"}
posts GET /posts(.:format) {:action=>"index", :controller=>"posts"}
POST /posts(.:format) {:action=>"create", :controller=>"posts"}
new_post GET /posts/new(.:format) {:action=>"new", :controller=>"posts"}
edit_post GET /posts/:id/edit(.:format) {:action=>"edit", :controller=>"posts"}
post GET /posts/:id(.:format) {:action=>"show", :controller=>"posts"}
PUT /posts/:id(.:format) {:action=>"update", :controller=>"posts"}
DELETE /posts/:id(.:format) {:action=>"destroy", :controller=>"posts"}
home_index GET /home/index(.:format) {:action=>"index", :controller=>"home"}
root /(.:format) {:action=>"index", :controller=>"home"}
try
= link_to "vote", incvotes_post_path(post), :method=>:post
and if that doesn't work, try changing the method to :put
My guess is that you probably do not have a definition in your routes file for the action you just defined in the controller. Both an action in the controller and an action in the routes file must be defined for Rails to generate urls correctly.
Your routes file probably has something like this:
resources :posts
But you want to add more than the standard actions generated by the resources keyword, so try something like this:
resources :posts do
member do
post 'incvotes'
end
end
This tells routes that you have another action in your posts controller called incvotes that accepts HTTP post requests as long as they are pointed at a member route with the correct action (/posts/14 is a member route, while /posts/ is a 'collection' route). So you will have a new route probably like /posts/14/incvotes that you can post a form to and everything should start working properly.
EDIT:
Actually I guess since you are just adding 1 to an attribute on a model, you don't need a POST action (which are normally associated with posting forms as with create and update). To send a post, you might need to change the HTML in the view to include a form and have it post to the correct url. So you can try that, or you can change your routes file to read get 'incvotes' instead of post 'incvotes'. Sorry for the confusion, hope that helps!
The incvotes_post route only accepts a HTTP POST, and a link always produces a HTTP GET.
Use a form with a button instead (or do a POST using AJAX).
Try using button_to instead link_to:
In your view:
<%= button_to 'Vote', incvotes_post_path(post) %>
In your config/routes.rb add the route to incvotes action as post:
resources :posts do
member do
post 'incvotes'
end
end
And in your controller, create the incvotes action:
def incvotes
# Something
redirect_to :posts
end
Lets say I am creating a new Foo using a form and a standard Rails restful controller, which looks something like this:
class FoosController < ApplicationController
...
def index
#foos = Foo.all
end
def new
#foo = Foo.new
end
def create
#foo = Foo.create(params[:foo])
if #foo.save
redirect_to foos_path, :notice => 'Created a foo.'
else
render 'new'
end
end
...
end
So, if I use the standard restful controller (as above), then when I'm creating the Foo I am at example.com/foos/new, and if I submit the form and it saves correctly I'm at example.com/foos showing the index action. However, if the form is not filled correctly the form is rendered again and error messages are shown. This is all plain vanilla.
However, if errors are shown, the form page will be rendered but the URL will be example.com/foos, because the CREATE action posts to that url. However, one would expect to find Foos#index at example.com/foos, not the form they just submitted now with error messages added.
This seems to be Rails standard behavior, but it doesn't make a lot of sense to me. Obviously I could redirect back to new instead of rendering new from the create action, but the problem with that is the error messages etc. would be lost along with the partially complete Foos in memory.
Is there a clean solution for this problem, a way to send people back to example.com/foos/new when there are errors in the new Foo form they submitted?
Thanks!
To answer your comment on another answer:
I'm wondering if there's a way, without rewriting the controller at all, to tell rails that you want the URL to match the rendered template, rather than the controller action that called it.
I don't think so; URLs are tied directly to routing, which is tied into a controller and action pair--the rendering layer doesn't touch it at all.
To answer your original question, here's information from another similar question I answered.
As you've found, by default when you specify resources :things, the POST path for creating a new thing is at /things. Here's the output for rake routes:
things GET /things(.:format) {:action=>"index", :controller=>"things"}
POST /things(.:format) {:action=>"create", :controller=>"things"}
new_thing GET /things/new(.:format) {:action=>"new", :controller=>"things"}
edit_thing GET /things/:id/edit(.:format) {:action=>"edit", :controller=>"things"}
thing GET /things/:id(.:format) {:action=>"show", :controller=>"things"}
PUT /things/:id(.:format) {:action=>"update", :controller=>"things"}
DELETE /things/:id(.:format) {:action=>"destroy", :controller=>"things"}
It sounds like you want something more like this:
create_things POST /things/new(.:format) {:action=>"create", :controller=>"things"}
things GET /things(.:format) {:action=>"index", :controller=>"things"}
new_thing GET /things/new(.:format) {:action=>"new", :controller=>"things"}
edit_thing GET /things/:id/edit(.:format) {:action=>"edit", :controller=>"things"}
thing GET /things/:id(.:format) {:action=>"show", :controller=>"things"}
PUT /things/:id(.:format) {:action=>"update", :controller=>"things"}
DELETE /things/:id(.:format) {:action=>"destroy", :controller=>"things"}
Although not recommended, you can get this result with the following route:
resources :things, :except => [ :create ] do
post "create" => "things#create", :as => :create, :path => 'new', :on => :collection
end
You would also need to modify your forms to make them POST to the correct path.
You could hook into rails routing by adding this in an initializer:
https://gist.github.com/903411
Then just put the regular resources in your routes.rb:
resources :users
It should create the routes and behaviour you are looking for.
You can set up the routing manually, if you're that concerned about what URL is going to show. For what you want, you can have a GET to /foos/new render your form, and a POST to the same URL do the creation:
map.with_options :controller => :foos do |foo|
foo.new_foo '/foos/new', :conditions => {:method => :get}, :action => :new
foo.create_foo '/foos/new', :conditions => {:method => :post}, :action => :create
foo.foos '/foos', :conditions => {:method => :get}, :action => :index
end
This should work without requiring any changes to your controller (yay!) - all three actions from your example are taken care of. The few disclaimers:
This is based on my routing for a 2.3.8 app - some syntax (semantics?) changes are probably required to get it into Rails 3 routing style.
My attempts to mix this style of routing with map.resources have failed horribly - unless you're more familiar with this than me, or Rails 3 routing is better (both easily possible), you'll have to do this for every route to the controller.
And finally, don't forget to add /:id, (.:format), etc. to the routes that need them (none in this example, but see #2).
Hope this helps!
Edit: One last thing - you'll need to hard-code the URL in your form_for helper on /foos/new.html.erb. Just add :url => create_foo_path, so Rails doesn't try to post to /foos, which it will by default (there might be a way to change the creation URL in the model, but I don't know of it, if there is one).
You could use Rack::Flash to store the parameters you wanted in the user's session and then redirect to your form url.
def create
#foo = Foo.new(params[:foo])
if #foo.save
redirect_to foos_path, :notice => 'Created a foo.'
else
flash[:foo] = params[:foo]
flash[:errors] = #foo.errors
redirect_to new_foo_path #sorry - can't remember the Rails convention for this route
end
end
def new
# in your view, output the contents of flash[:foo]
#foo = Foo.new(flash[:foo])
end