Ben Muschol
Ben Muschol

Reputation: 607

Making a form to update multiple Users at once

I'm trying to make a form that updates the values of multiple users with one submit button. Here's my (obviously misguided) attempt:

<h1>Set Days</h1>
<%= form_for User do |f| %>
  <% f.fields_for :all do |user_form| %>
    <div class="row">
      <div class="col-md-3">
        <span><%= user_form.label :name %></span>
      </div>
      <div class="col-md-9">
        <%= f.fields_for user.name do |user_form| %>
          <table class="days-table">
            <tr>
              <td>M</td>
              <td>Tu</td>
              <td>W</td>
              <td>Th</td>
              <td>F</td>
            </tr>
            <tr>
              <td><%= user_form.check_box :monday, {}, true, false %></td>
              <td><%= user_form.check_box :tuesday, {}, true, false %></td>
              <td><%= user_form.check_box :wednesday, {}, true, false %></td>
              <td><%= user_form.check_box :thursday, {}, true, false %></td>
              <td><%= user_form.check_box :friday, {}, true, false %></td>
            </tr>
          </table>
        <% end %>
      </div>
    </div>
  <% end %>
  <%= f.submit "Save", class: "btn btn-primary" %>
<% end %>

I get this error with this code:

undefined method `to_key' for #<Class:0x007fdcda98dc08>

Upvotes: 1

Views: 124

Answers (1)

tompave
tompave

Reputation: 12412

You are getting that specific error because, under the hood, Rails' form_for(record, options = {}, &block) will try to send a to_key message to the first argument.

To accomplish what you're trying to do, you'll be better off with form_tag.

Updating multiple records at once is not RESTful.

For example, with one record the convention is:

PATCH  /resources/:id   { new data payload... }
# you're updating a specific resource

With multiple records you can't rely on a conventional REST endpoint, and need to let the server logic handle the process. For example:

POST /resources   { ids: [x, y, z], data: {...} }

So, build your form without associating it to a specific record (user form_tag, not form_for), and add to the POST request a list of IDs for the records you want to update.

Just be careful and take into account that this exposes you to attacks. What if a user decides to tweak the html of the form to update other records? You should use some authorization technique on the server side, or just not add that list in the form.

Then, on the server side, you can use something like this:

Resource.where(id: params[:id_list]).update_all(sanitized_params)

def sanitized_params
  params[:my_updates].your_sanitization_logic
end

Or, if your resources are already associated to the current user, this is a common pattern:

current_user.resources.where(id: params[:id_list]).update_all(sanitized_params)

Upvotes: 1

Related Questions