Paperclip, Multiple Image Uploads, Rails 2.3.4
Hello folks,
It’s been a while since I updated the blog and rails has changed a bit since then. I thought I’d revise one of my previous posts: the one about uploading multiple images using paperclip. This time we’re going to be doing it using Rails 2.3.4. I’m going to be using the paperclip gem, as well as the nifty_generator gem(for ease of demonstration). Just google them if you don’t know what they are or if you’re new to the rails world
Lets start off by installing the necessary gems.
gem sources -a http://gems.github.com sudo gem install thoughtbot-paperclip sudo gem install gem install nifty-generators
Create our Rails app and generate a few things for us to play with.
rails demo # remove index file from the public directory script/generate nifty_layout script/generate nifty_scaffold Album name:string # config/routes.rb map.root :controller => 'albums' script/generate model Photo album_id:integer script/generate paperclip photo data rake db:migrate script/server
Now open up the form partial in app/views/albums and modify it like so.
<% form_for @album, :html => { :multipart => true } do |f| %> <%= f.error_messages %> <p> <%= f.label :name %><br /> <%= f.text_field :name %> </p> <div id="photos"> <% if @album.new_record? %> <%= render :partial => 'photo', :locals => { :form => f, :photo => @album.photos.build } %> <% end %> </div> <%= add_object_link("New Photo", f, @album.photos.build, "photo", "#photos") %> <p><%= f.submit "Submit" %></p> <% end %>
Then add a new partial under app/views/albums called photo:
<div class="photo"> <p> <% form.fields_for :photos, photo, :child_index => (photo.new_record? ? "index_to_replace_with_js" : nil) do |photo_form| %> <%= photo_form.file_field :data %> <%= link_to_function "delete", "remove_field($(this), ('.photo'))" %><br/> <% end %> </p> </div>
Add the necessary Javascript methods so that we can generate photo file upload fields.
# in application.js
function remove_field(element, item) {
element.up(item).remove();
}
# download the jquery library to your javascripts folder and then modify application.html.erb layout file.
<head>
<title><%= h(yield(:title) || "Untitled") %></title>
<%= stylesheet_link_tag 'application' %>
<%= javascript_include_tag :defaults, 'jquery-1.3.2.min' %>
<script>
jQuery.noConflict();
</script>
<%= yield(:head) %>
</head>Add the following helper methods to albums_helper.rb. This bit was modified from apidock, rails, fields_for section.
def add_object_link(name, form, object, partial, where) html = render(:partial => partial, :locals => { :form => form}, :object => object) link_to_function name, %{ var new_object_id = new Date().getTime() ; var html = jQuery(#{js html}.replace(/index_to_replace_with_js/g, new_object_id)).hide(); html.appendTo(jQuery("#{where}")).slideDown('slow'); } end def js(data) if data.respond_to? :to_json data.to_json else data.inspect.to_json end end
Now add the following to your models.
# photo class require 'paperclip' class Photo < ActiveRecord::Base belongs_to :album has_attached_file :data, :styles => { :medium => "300x300>", :thumb => "100x100>" } validates_attachment_content_type :data, :content_type => 'image/jpeg', :message => "has to be in jpeg format" end # album class class Album < ActiveRecord::Base attr_accessible :name validates_presence_of :name has_many :photos, :dependent => :destroy accepts_nested_attributes_for :photos end
Notice the accepts_nested_attributes_for in the album class? That pretty much takes care of a plethora of code we would otherwise have to add to facilitate nested model behavior.
Now add this to your show.html.erb under apps/views/albums and you’re done.
<% title "Album" %> <p> <strong>Name:</strong> <%=h @album.name %> </p> <% for photo in @album.photos %> <p><%= image_tag(photo.data(:thumb)) %></p> <% end %> <p> <%= link_to "Edit", edit_album_path(@album) %> | <%= link_to "Destroy", @album, :confirm => 'Are you sure?', :method => :delete %> | <%= link_to "View All", albums_path %> </p>
That’s it folks. Enjoy and feel free to ask any questions or drop any comments!
Recent Comments




