RailsCasts - Ruby on Rails Screencasts

RailsCasts Pro episodes are now free!

Learn more or hide this

Edit Multiple (revised)

#165 Edit Multiple (revised)

Feb 25, 2013 | 15 minutes | Forms
Editing one record at a time can be tedious work. Here you will learn three different ways to edit multiple records at once using checkboxes.
Click to Play Video ▶
Tweet
  • Download:
  • source code Project Files in Zip (130 KB)
  • mp4 Full Size H.264 Video (34.4 MB)
  • m4v Smaller H.264 Video (18.1 MB)
  • webm Full Size VP8 Video (23.3 MB)
  • ogv Full Size Theora Video (42.5 MB)
Browse_code Browse Source Code

Resources

Solution 1: Inline

products/index.html.erb
<%= form_tag discontinue_products_path, method: :put do %>
 ...
 <%= check_box_tag "product_ids[]", product.id %>
 ...
 <%= submit_tag "Discontinue Checked" %>
<% end %>
config/routes.rb
resources :products do
 collection do
 put :discontinue
 end
end
products_controller.rb
def discontinue
 Product.update_all({discontinued: true}, {id: params[:product_ids]})
 redirect_to products_url
end

Solution 2: Edit Individually

products/index.html.erb
<%= form_tag edit_multiple_products_path, method: :get do %>
 ...
 <%= check_box_tag "product_ids[]", product.id %>
 ...
 <%= submit_tag "Edit Checked" %>
<% end %>
config/routes.rb
resources :products do
 collection do
 get :edit_multiple
 put :update_multiple
 end
end
products_controller.rb
def edit_multiple
 @products = Product.find(params[:product_ids])
end
def update_multiple
 @products = Product.update(params[:products].keys, params[:products].values)
 @products.reject! { |p| p.errors.empty? }
 if @products.empty?
 redirect_to products_url
 else
 render "edit_multiple"
 end
end
products/edit_multiple.html.erb
<%= form_tag update_multiple_products_path, method: :put do %>
 <% @products.each do |product| %>
 <h2><%= product.name %></h2>
 <%= fields_for "products[]", product do |f| %>
 <% if product.errors.any? %>
 <div id="error_explanation">
 <h2><%= pluralize(product.errors.count, "error") %> prohibited this product from being saved:</h2>
 <ul>
 <% product.errors.full_messages.each do |msg| %>
 <li><%= msg %></li>
 <% end %>
 </ul>
 </div>
 <% end %>
 <div class="field">
 <%= f.label :name %><br />
 <%= f.text_field :name %>
 </div>
 ...
 <% end %>
 <% end %>
 <div class="actions">
 <%= submit_tag "Update" %>
 </div>
<% end %>

Solution 3: Single Fieldset

products/index.html.erb
<%= form_tag edit_multiple_products_path, method: :get do %>
 ...
 <%= check_box_tag "product_ids[]", product.id %>
 ...
 <%= submit_tag "Edit Checked" %>
<% end %>
config/routes.rb
resources :products do
 collection do
 get :edit_multiple
 put :update_multiple
 end
end
products_controller.rb
def edit_multiple
 @products = Product.find(params[:product_ids])
end
def update_multiple
 @products = Product.find(params[:product_ids])
 @products.reject! do |product|
 product.update_attributes(params[:product].reject { |k,v| v.blank? })
 end
 if @products.empty?
 redirect_to products_url
 else
 @product = Product.new(params[:product])
 render "edit_multiple"
 end
end
products/edit_multiple.html.erb
<%= form_tag update_multiple_products_path, method: :put do %>
 <ul>
 <% @products.each do |product| %>
 <li>
 <%= hidden_field_tag "product_ids[]", product.id %>
 <%= product.name %>
 <ul class="errors">
 <% product.errors.full_messages.each do |msg| %>
 <li><%= msg %></li>
 <% end %>
 </ul>
 </li>
 <% end %>
 </ul>
 <%= fields_for :product do |f| %>
 <div class="field">
 <%= f.label :name %><br />
 <%= f.text_field :name %>
 </div>
 <div class="field">
 <%= f.label :price_modification %><br />
 <%= f.text_field :price_modification %>
 </div>
 <div class="field">
 <%= f.label :category_id %><br />
 <%= f.collection_select :category_id, Category.order("name"), :id, :name, include_blank: true %>
 </div>
 <div class="field">
 <%= f.label :discontinued %><br />
 <%= f.select :discontinued, [["Yes", true], ["No", false]], include_blank: true %>
 </div>
 <% end %>
 <div class="actions">
 <%= submit_tag "Update" %>
 </div>
<% end %>
models/product.rb
attr_accessible :name, :price, :discontinued, :category_id, :price_modification
validates_numericality_of :price
attr_reader :price_modification
def price_modification=(new_price)
 @price_modification = new_price
 if new_price.to_s.ends_with? "%"
 self.price += (price * (new_price.to_d/100)).round(2)
 else
 self.price = new_price
 end
end
loading

AltStyle によって変換されたページ (->オリジナル) /