Posts
has_many file attachments using Paperclip
16 Jun 2010
Tags:
gem, paperclip, has_many, multiple file attachments
I decided to use paperclip instead of attachment_fu for our new app. This is just a procedure on how to use paperclip with a has_many association.
We create two models: post and post_photo
>> ruby script/generate model post title:text body:text >> ruby script/generate model post_photo post_id:integer
and associate them as follows.
class Post < ActiveRecord::Base has_many :post_photos end class PostPhoto < ActiveRecord::Base belongs_to :post has_atttached_file :photo end
Using paperclip's generator we create a migration to add columns to post_photo
>> ruby script/generate paperclip post_photo photo
After that, create your views and controller methods. (Btw, I use haml for the views. I also like creating an associated record using virtual attributes. I also used the prototype based js, Builder, to create dom elements.)
# Post#new.html.haml - form_for @post do |f| %label Title = f.text_field :post %label Body = f.text_area :body %label Photos = file_field_tag 'post[photo_attributes][]' #new_photos = link_to_function 'Add', 'add_file_field()' :javascript function add_file_field() { var div = Builder.new('div'); var file = Builder.new('input', {type:'file', name:'post[photo_attributes][]'}); var link = Builder.new('a', {onclick:'$("this").up().remove();}, 'Remove'); $('div').appendChild(file); $('div').appendChild(link); $('new_photos').appendChild(div);
# PostsController def create @post = Post.new(params[:post]) if @post.save flash[:notice] = 'Post saved' redirect_to @post else render :action => :new end end # post.rb def photo_attributes=(attribs) attribs.each do |attrib| post_photos.build :photo => attrib end end
multiple image upload with preview using ajax and jquery
04 Jul 2010
Tags:
paperclip, preview, ajax, jquery, multiple image upload
In this tutorial, we will use jquery to upload multiple images using ajax and show a preview of the images. We'll also use the paperclip gem to store the files. You can look at how to add paperclip to your project .
First we generate the image model and set it to use paperclip. We'll also create a photo controller
>> script/generate model photo >> script/generate paperclip photo photo >> rake db:migrate >> script/generate controller photos
# app/models/photo.rb class Photo < ActiveRecord::Base has_attached_file :photo end
Next, we setup the view and javascript for ajaxupload.
Again, I'm using haml instead of erb templates, and builder for creating DOM elements.
And since we're using prototype and jquery in the same page, we need to make sure that there's no conflict between the two libraries. Add the following code in the head tag of your page.
= javascript_include_tag :defaults = javascript_include_tag 'builder', 'jquery-1.4.2.min', 'ajaxupload' :javascript var $j = jQuery.noConflict();
And here's the view
# app/views/photos/new.html.haml - form_tag photos_path, :method => :post do #photos #file_upload %label Add a Photo = file_field_tag :photo, '', :id => 'file_tag' = image_tag '/images/spinner.gif', :id => 'spinner', :style => 'display:none' = submit_tag 'Save Photos' :javascript $j(document).ready(function(){ new AjaxUpload('file_tag', { action: '/photo_preview?authenticity_token=' + encodeURIComponent(#{form_authenticity_token.inspect}), name: 'image', onSubmit: function(file, extension) { $('spinner').show(); }, onComplete: function(file, response) { $('spinner').hide(); show_preview(response); } }); }); function show_preview(path) { var parent = Builder.node('div', {class:'preview'}); var image = Builder.node('img', {src:path, width:'80px'}); var hidden = Builder.node('input', {type:'hidden', value:path, name:'photo[]'}); parent.appendChild(image); parent.appendChild(hidden); $('photos').appendChild(parent); }
Now we need to create the photo_preview and create action in photos controller
# app/controllers/photos_controller.rb # of course, this code won't work if the user uploads different photos with the same filename def create photos = params[:paths].collect {|p| {:photo => File.new('public' + p)}} @photos = Photo.create(photos) # now do whatever you wish with @photos end def photo_preview if params[:image] filename = params[:image].original_filename path = '/images/' + filename f = File.new('public' + path, 'w') {|f| f.write params[:image].read} render :text => path end end