简体   繁体   中英

How do I properly create a custom action in Rails?

I'm trying to create custom form that allows admins to toggle the user privileges of users on and off, and save them to the database. I must be doing something completely wrong, because the pages generate fine, and the submit button submits to the action fine, but nothing gets saved to the database, and it just renders my initial view again. Can anyone see what I'm doing wrong?

The Code

Routes:

resources :users do
  member do
    get 'assign'
    put 'assign_update'
  end
end
...

Controller (This weird way of doing it is an attempt to circumvent the fact that the admin and other attributes are not accessible. It might be a mess.):

...
def assign
  @user = User.find(params[:id])
end

def assign_update
  admin_protected = params[:user].delete(:admin)

  @user = User.find(params[:id])
  @user.admin = admin_protected

  if @user.save
    flash[:success] = "User updated"
    redirect_to users_path
  else
    render 'assign'
  end
end

View:

...
<%= form_for(@user, url: { controller: 'users', 
  action: 'assign_update'}, method: 'put') do |f| %>
  <%= f.label :admin, 'Is admin?', class: 'checkbox inline' %>
  <%= f.check_box :admin %>
  <%= f.submit "Save changes", class: "btn btn-large btn-primary" %>
<% end %>

So to summarize the conversation we had in the comment section under the question...

Problem was found to be validations on other user model attributes. The user instance couldn't be saved because validations were not passing when save method was being executed. As a result, Rails was just rendering assign view.

update_attribute method updates an attribute without model validation nor mass assignment protection. And admin attribute fits those two criteria in this case.

You should be using attr_accessible in your model and manually picking those fields from the params has and assigning them individually making sure that the admin field is NOT included in the list of fields assigned to the attr_accessible declaration

So

def assign_update
  admin_protected = params[:user].delete(:admin)

  @user = User.find(params[:id])
  @user.admin = admin_protected

  if @user.save
    flash[:success] = "User updated"
    redirect_to users_path
  else
    render 'assign'
  end
end

becomes

    def assign_update
#      admin_protected = params[:user].delete(:admin)

      @user = User.find(params[:id])
      @user.admin = params[:user][:admin]

      if @user.save
        flash[:success] = "User updated"
        redirect_to users_path
      else
        render 'assign'
      end
    end

The problem is that in your approach you are still mass assigning.

To debug what is actually happening you should take a careful look at you log file output

UPDATE

Check the errors list. Add the following to your form

  <% if @user.errors.any? %>
    <div id="error_explanation">
      <h2><%= pluralize(@user.errors.count, "error") %> prohibited this account from being saved:</h2>

      <ul>
      <% @user.errors.full_messages.each do |msg| %>
        <li><%= msg %></li>
      <% end %>
      </ul>
    </div>
  <% end %>

It should give you, ad your users a clearer idea of what is wrong and how to fix it

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM