erb ruby 88 lines · 3 tabs

Bulk-Updating Order Statuses with a Checkbox Collection and update_all Batch Action in Rails

Shared by codesnips Sep 2026
3 tabs
<%= form_with url: bulk_update_orders_path, method: :patch, data: { turbo_confirm: "Update selected orders?" } do %>
  <div class="batch-toolbar">
    <%= select_tag :status,
          options_for_select(Order.statuses.keys.map { |s| [s.humanize, s] }),
          prompt: "Set status to\u2026" %>
    <%= submit_tag "Apply to selected", class: "btn" %>
  </div>

  <table class="orders">
    <thead>
      <tr><th></th><th>Order</th><th>Customer</th><th>Status</th></tr>
    </thead>
    <tbody>
      <% @orders.each do |order| %>
        <tr>
          <td>
            <%= check_box_tag "order_ids[]", order.id, false,
                  disabled: !order.updatable?, id: dom_id(order, :select) %>
          </td>
          <td>#<%= order.number %></td>
          <td><%= order.customer_name %></td>
          <td><span class="badge badge-<%= order.status %>"><%= order.status.humanize %></span></td>
        </tr>
      <% end %>
    </tbody>
  </table>
<% end %>
3 files · erb, ruby Explain with highlit

This snippet shows a batch-action pattern in Rails where a user selects several orders via checkboxes and transitions them to a new status in a single request. The point is to do the work as one set-based UPDATE rather than looping and saving each record, which is faster and avoids N callbacks firing per row.

The orders/index view renders a form_with posting to a member-less collection route (bulk_update_orders_path). Each row includes check_box_tag with the shared name order_ids[], so the browser submits an array of selected primary keys. A select_tag lets the operator choose the target status, and the same form is reused for filtering — the important detail is that the checkbox name uses [] so Rails parses params[:order_ids] into an array.

In OrdersController, bulk_update reads and sanitizes those params. bulk_update_params whitelists the incoming array and coerces the ids to integers, while assert_valid_status! guards against arbitrary status strings by checking against Order.statuses, the hash defined by the enum. The scope Order.where(id: ids) is narrowed further by updatable so already-shipped or cancelled orders are excluded. The call to .update_all writes every matching row in one statement and returns the affected count, which is surfaced back to the operator via flash. Because update_all bypasses validations and callbacks, updated_at is set explicitly so timestamps stay honest.

The Order model defines the status enum and an updatable scope listing the states that may still transition. The bulk_transition_to class method centralizes the guard-plus-update_all logic so it is testable in isolation and reusable from a background job. The trade-off of update_all is real: no paper_trail versions, no after_update hooks, no email side effects — so anything requiring per-record logic must be enqueued separately, which bulk_transition_to does by collecting ids before the write. This pattern fits admin dashboards and moderation queues where throughput matters and the transition is a simple column change. A pitfall to watch is that update_all does not run optimistic-locking checks, so concurrent edits can be silently overwritten; scoping by updatable limits that blast radius.


Related snips

Share this code

Here's the card — post it anywhere.

Bulk-Updating Order Statuses with a Checkbox Collection and update_all Batch Action in Rails — share card
Link copied