Creating a Ruby on Rails admin form to bulk update multiple models
For my rails photo gallery site I needed the ability to bulk add descriptions and tags to my images. Finding, loading, editing, saving each photo individually is painful. I decided to create a form to edit a page of images at one time.
Added a ‘get’ route for the form. edited file: config/routes.rb
get 'admin/images-bulk-update' => 'images#bulk_update', :as => 'admin_images_bulk_update'
Added the bulk_update method to the images controller. Edited file: app/controllers/images_controller.rb
class ImagesController < ApplicationController
def bulk_update
@images = current_user.images.order("created_at desc").page params[:page]
end
end
Add the view for the bulk_update action. The form_tag posts to another controller action. The each loop shown below outputs the tabular image data, and the fields_for method uses a partial to render the form fields. file: app/views/images/bulk_update.erb
<%= form_tag admin_images_bulk_update_save_path :method => :post do %>
<%= hidden_field_tag 'page', (params[:page].nil? ? 1 : params[:page]) %>
<table class='table table-striped table-condensed'>
<thead>
<tr>
<th>ID</th>
<th>Thumb</th>
<th>Title</th>
<th>Description</th>
<th>Tags</th>
<th>Album</th>
</tr>
</thead>
<tbody>
<% @images.each do |image| %>
<%= fields_for "images[]", image do |f| %>
<%= render :partial => 'bulk_update_form_fields', :locals => {:f => f, :image => image} %>
<% end %>
<% end %>
</tbody>
</table>
<p><%= submit_tag "Update Images", :class => 'btn btn-success' %></p>
<% end %>
<%= paginate @images %>
The field_for renders the partial: bulk_update_form_fields. This partial simply outputs the form elements. file: app/views/images/_bulk_update_form_fields.html/erb
<tr>
<td>
<%= image.id %>
</td>
<td>
<%= image_tag image.upload.url(:thumb) %>
</td>
<td>
<%= f.text_field :title %>
</td>
<td>
<%= f.text_field :description %>
</td>
<td>
<%= f.text_field :tag_list %>
</td>
<td>
<%= image.album.title %>
</td>
</tr>
The above form submits to “admin_images_bulk_update_save_path”. The post route for the form. edited file: config/routes.rb
post 'admin/images-bulk-update-save' => 'images#bulk_update_save', :as => 'admin_images_bulk_update_save'
Added the bulk_update_save method to the images controller. The update method on the Image object accepts the keys and values from the submitted form data to update each Image model. The rest of the code collects errors, and redirects the user back to the form action. edited file: app/controllers/images_controller.rb
class ImagesController < ApplicationController
def bulk_update_save
result = Image.update(params[:images].keys, params[:images].values).reject { |p| p.errors.empty? }
if result.empty?
flash[:notice] = "Images updated"
redirect_to admin_images_bulk_update_path(:page => params[:page])
else
image_ids = result.collect {|i| i.id}
flash[:error] = "Error(s) occurred updating image(s): #{image_ids.join ', '}"
redirect_to admin_images_bulk_update_path(:page => params[:page])
end
end
end
Last I added a before_filter method to authenticate admin users for these controller actions. edited file: app/controllers/images_controller.rb
class ImagesController < ApplicationController
before_filter :auth_bulk_update, :only => [:bulk_update, :bulk_update_save]
private
def auth_bulk_update
# authentication code here
end
end
The end result is a form that allows me to bulk edit my photos with pagination.