Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Rails 3 Form For Custom Action

I'm having trouble routing a form to a custom action in Rails 3. Here are my routes:

resources :photos do
    resources :comments
    collection do
        get 'update_states'
    end
    member do
        put 'upload'
    end
end

Here's the form_for:

form_for @photo, :remote => true, :url => { :action => upload_photo_path(@photo) }, :html => { :multipart => :true, :method => 'put' } do |f|

And here's the error message:

No route matches {:action=>"/photos/42/upload", :controller=>"photos"}

... this is especially frustrating because "photos/:id/upload" is exactly the correct action for this form.

What am I missing?

EDITS - Here are the original Photo-related routes:

   photo_comments    GET    /photos/:photo_id/comments(.:format)          {:action=>"index", :controller=>"comments"}
                     POST   /photos/:photo_id/comments(.:format)          {:action=>"create", :controller=>"comments"}
   new_photo_comment GET    /photos/:photo_id/comments/new(.:format)      {:action=>"new", :controller=>"comments"}
  edit_photo_comment GET    /photos/:photo_id/comments/:id/edit(.:format) {:action=>"edit", :controller=>"comments"}
       photo_comment GET    /photos/:photo_id/comments/:id(.:format)      {:action=>"show", :controller=>"comments"}
                     PUT    /photos/:photo_id/comments/:id(.:format)      {:action=>"update", :controller=>"comments"}
                     DELETE /photos/:photo_id/comments/:id(.:format)      {:action=>"destroy", :controller=>"comments"}
update_states_photos GET    /photos/update_states(.:format)               {:action=>"update_states", :controller=>"photos"}
        upload_photo PUT    /photos/:id/upload(.:format)                  {:action=>"upload", :controller=>"photos"}
              photos GET    /photos(.:format)                             {:action=>"index", :controller=>"photos"}
                     POST   /photos(.:format)                             {:action=>"create", :controller=>"photos"}
           new_photo GET    /photos/new(.:format)                         {:action=>"new", :controller=>"photos"}
          edit_photo GET    /photos/:id/edit(.:format)                    {:action=>"edit", :controller=>"photos"}
               photo GET    /photos/:id(.:format)                         {:action=>"show", :controller=>"photos"}
                     PUT    /photos/:id(.:format)                         {:action=>"update", :controller=>"photos"}
                     DELETE /photos/:id(.:format)                         {:action=>"destroy", :controller=>"photos"}

Here are the relevant routes when I changed the route to match 'upload':

 photo_comments GET    /photos/:photo_id/comments(.:format)          {:action=>"index", :controller=>"comments"}
                     POST   /photos/:photo_id/comments(.:format)          {:action=>"create", :controller=>"comments"}
}
   new_photo_comment GET    /photos/:photo_id/comments/new(.:format)      {:action=>"new", :controller=>"comments"}
  edit_photo_comment GET    /photos/:photo_id/comments/:id/edit(.:format) {:action=>"edit", :controller=>"comments"}
       photo_comment GET    /photos/:photo_id/comments/:id(.:format)      {:action=>"show", :controller=>"comments"}
                     PUT    /photos/:photo_id/comments/:id(.:format)      {:action=>"update", :controller=>"comments"}
                     DELETE /photos/:photo_id/comments/:id(.:format)      {:action=>"destroy", :controller=>"comments"}
update_states_photos GET    /photos/update_states(.:format)               {:action=>"update_states", :controller=>"photos"}
        upload_photo        /photos/:id/upload(.:format)                  {:action=>"upload", :controller=>"photos"}
              photos GET    /photos(.:format)                             {:action=>"index", :controller=>"photos"}
                     POST   /photos(.:format)                             {:action=>"create", :controller=>"photos"}
           new_photo GET    /photos/new(.:format)                         {:action=>"new", :controller=>"photos"}
          edit_photo GET    /photos/:id/edit(.:format)                    {:action=>"edit", :controller=>"photos"}
               photo GET    /photos/:id(.:format)                         {:action=>"show", :controller=>"photos"}
                     PUT    /photos/:id(.:format)                         {:action=>"update", :controller=>"photos"}
                     DELETE /photos/:id(.:format)                         {:action=>"destroy", :controller=>"photos"}

Unfortunately 'match' didn't work any better...

-- EDIT --

Just to confirm another scenario here... with this in the routes:

resources :photos do
    resources :comments
    collection do
        get 'update_states'
    end
    member do
        match 'upload'
    end
end

and this in the view:

form_for @photo, :remote => true, :url => { :action => 'upload' }, :html => { :multipart => :true, :id => 'photo_upload' } do |f|

I still get:

No route matches {:action=>"upload", :controller=>"photos"}

like image 204
Andrew Avatar asked Jan 15 '11 00:01

Andrew


3 Answers

What if you did just :url => upload_photo_path(@photo)?

It seems a little strange that you'd be uploading to a member though. Is this just a creation method (in which case you should just POST to the collection path)?

like image 171
davemyron Avatar answered Nov 16 '22 17:11

davemyron


I had the same problem and finally worked through to a solution which I'm not sure was reached in the above case, since the original poster moved on to another approach.

My routes had

resources :members  do
  member do
    get "invite" 
    post 'register'
  end
end

And "rake routes" included

register_member POST   /members/:id/register(.:format)    {:protocol=>"http", :action=>"register", :controller=>"members"}

Yet I kept getting the error

Started POST "/members/149/register" for 127.0.0.1 at 2012-04-13 13:18:35 -0700

ActionController::RoutingError (No route matches "/members/149/register"):

Rendered /Users/lisa/.rvm/gems/ruby-1.9.2-p180@stv/gems/actionpack-3.0.9/lib/action_dispatch/middleware/templates/rescues/routing_error.erb within rescues/layout (1.1ms)

and the problem was limited only to the form generated by Rails according to the below form_for (note I confirmed this using HTTP client to manually POST to the URL and saw it was finding the route)

<%= form_for @account, :url => register_member_path(@account.id) do |account_form| %>
   ... 

I checked the method, I checked the path, everything looked good. What I finally noticed, scouring the generated form line by line:

<form accept-charset="UTF-8" action="/members/149/register" class="edit_member" id="edit_member_149" method="post">
  <div style="margin:0;padding:0;display:inline">
    <input name="utf8" type="hidden" value="&#x2713;" />
    <input name="_method" type="hidden" value="put" />
    <input name="authenticity_token" type="hidden" value="74pkMgRHfdowSfzjJGMILkAsivVNrJZ0iWYXRUgxyF0=" />
  </div>
...

Notice the hidden input name="_method". I wish the fact that Rails was interpreting this as a PUT had shown up in the logs, that would have made my debugging a lot faster. I fixed it by telling the form explicitly to use the POST method, which of course it was already, but telling it that removed the hidden _method override. I assume there's some facet about @account which triggered Rails to use the _method parameter, but @account should be an existing record.

like image 26
LisaD Avatar answered Nov 16 '22 15:11

LisaD


Your url parameter should be

:url => { :action => "upload" }


(Original reply)

I would bet it's because your route expects a PUT and your form is sending a POST (probably because @photo = Photo.new). You have several options:

  1. Change your route to post 'upload'
  2. Create your form with form_for @photo, :as => :post with the rest of your arguments
  3. Make sure @photo is an existing record (e.g. by calling create instead of new)

The most appropriate choice is probably one of the first 2.

like image 1
David Sulc Avatar answered Nov 16 '22 16:11

David Sulc