Toggling a single value in a model, all from a link_to
I have an Admin controller for a page of a Rails 3 app that lists all the User model accounts in a table, and part of each row should be a link (with confirmation) to toggle the is_admin attribute of my User model. I would like to do this by allowing an admin to click on text in the table cell that lists the current admin status (i.e. clicking on 'No' will set is_admin to true).
For now, I'm not interested in any fancy AJAX approach, and let's disregard the fact that from a UI perspective, clicking on text indicating the state of an object to toggle that state isn't necessarily the best choice.
What I'm wondering is, can this开发者_C百科 be done within a single link_to call, or do I need to construct a form around it? My admin controller is defined as 'resources :admin' in routes.rb and rake routes
lists a PUT action with the named path of 'admin' that invokes the admin#update method.
Here's what I've got so far:
<%= link_to (user.is_admin ? "Yes" : "No"),
admin_path(:user => {:id => user.id, :is_admin => !user.is_admin}),
{:confirm => "Are you sure?", :method => :put} %>
My thinking is I'm passing in enough data in a RESTful call to the update method so that I can modify just that attribute. I realize it would be possible to just define a method called toggle_admin
, pass along just the ID and operate on that member of the model, but I thought this approach would be as RESTful as possible, and I'm more interested in learning the extent to wich I can use link_to here.
My error that I'm getting is:
No route matches {:action=>"destroy", :controller=>"admin", :user=>{:id=>1, :is_admin=>false}}
I'm not sure why it's trying to invoke the destroy method, it seems to be the last thing in my list of admin specific routes:
rake routes | grep admin
site_admin /admin(.:format) {:controller=>"admin", :action=>"index"}
admin_index GET /admin(.:format) {:action=>"index", :controller=>"admin"}
admin_index POST /admin(.:format) {:action=>"create", :controller=>"admin"}
new_admin GET /admin/new(.:format) {:action=>"new", :controller=>"admin"}
edit_admin GET /admin/:id/edit(.:format) {:action=>"edit", :controller=>"admin"}
admin GET /admin/:id(.:format) {:action=>"show", :controller=>"admin"}
admin PUT /admin/:id(.:format) {:action=>"update", :controller=>"admin"}
admin DELETE /admin/:id(.:format) {:action=>"destroy", :controller=>"admin"}
I thought perhaps I'm passing the method wrong and it's defaulting to the last one, but this page suggests that you can set :method =>
with your :confirm
e.g. as an HTML option.
So fundamentally, my question is if I'm supposed to be able to make a RESTful non GET call via link_to like this? I assumed that Rails would just build a little mini form behind the scenes for me.
More source code in this gist.
Use this:
div_for @user do
link_to (@user.is_admin? ? "Yes" : "No"), [@user, :toggle], :confirm => ...
end
and this route:
map.resources :users, :as => "admin", :member => { :toggle => :put }
and this controller method:
class UsersController < ...
def toggle
@user = User.find params[:id], :lock => true
@user.is_admin = !@user.is_admin?
@user.save!
if request.xhr?
render :update {|page| page[@user].replace_html @user }
else
redirect_to @user
end
end
end
Trying to handle this with a link to the update action gets just too complicated for just one case (as you already figured out there are some pitfalls). Using link_to_remote
for this case is a better candidate (just in case I already added the ajax handling to show it's not too fancy).
精彩评论