How do you design your model for multiple upload?
Posted by
questioner
on
August 16, 2010
I am designing a system that contains a lot of upload assets, such as model Post has_one Video and/or has_many Images, model Site has_one Logo and model Question has_many Images.
How do you design the models for such system? I don't care which upload plugin you use.
Answers
Answered by
eric
on
August 16, 2010
You should use the nested form functionality that Rails provides. Then you can use whatever plugin you want (I prefer paperclip myself).
Answered by
flyerhzm
on
August 17, 2010
I always use STI + polymorphic model for multiple uploads with paperclip.
I use STI and polymorphic model because I save all the uploaded assets in assets table and reuse the Asset model.
For your case, I will define the Asset models as follows
class Asset < ActiveRecord::Base
belongs_to :assetable, :polymorphic => true
end
class Post::Video < Asset
has_attached_file :attachment, :processors => [:flash], :styles => {:default => ["400x300>", "flv"]}
end
class Post::Image < Asset
has_attached_file :attachment, :styles => { :small => "200x150>", :large => "400x300>" }
end
class Site::Logo < Asset
has_attached_file :attachment, :styles => { :default => "64x64>" }
end
class Question::Image < Asset
has_attached_file :attachment, :styles => { :small => "200x150>", :large => "400x300>" }
end
As you seen, all the uploaded assets (including video and images of post, logo of site and images of question) are saved in assets table by STI model. The video upload is not supported by paperclip, you should define your own processor to process the video. The assets table definition is like
create_table :assets, :force => true do |t|
t.string :type
t.integer :assetable_id
t.string :assetable_type
t.string :attachment_file_name
t.string :attachment_content_type
t.integer :attachment_file_size
t.datetime :attachment_updated_at
end
The column type is for STI, so you can save Post::Video, Post::Image, Site::Logo and Question::Image in one assets table, and the assetable_id and assetable_type are for polymorphic, so you can reuse the Asset model in Post, Site and Question models.
So the relationships between asset and post, site, question are polymorphic as follow
class Post < ActiveRecord::Base
has_one :video, :as => :assetable, :class_name => "Post::Video", :dependent => :destroy
has_many :images, :as => :assetable, :class_name => "Post::Image", :dependent => :destroy
accepts_nested_attributes_for :video, :images
end
class Site < ActiveRecord::Base
has_one :logo, :as => :assetable, :class_name => "Site::Logo", :dependent => :destroy
accepts_nested_attributes_for :logo
end
class Question < ActiveRecord::Base
has_many :images, :as => :assetable, :class_name => "Question::Image", :dependent => :destroy
accepts_nested_attributes_for :images
end
Be attention that I add the accepts_nested_attributes_for for models who needs to upload assets so that I can easily to create or update object and assets with nested form.
<%= form_for @post, :html => {:multipart => true} do |form| %>
<p>
<%= form.label :name %>
<%= form.text_field :name %>
</p>
<%= form.fields_for :video do |video_form| %>
<p>
<%= logo_form.label :video %>
<%= logo_form.file_field :attachment %>
</p>
<% end %>
<%= form.fields_for :images do |image_form| %>
<p>
<%= image_form.label :image %>
<%= image_form.file_field :attachment %>
</p>
<% end %>
<p>
<%= form.submit %>
</p>
<% end %>
Before you handle the form, be sure you have build assets objects, such as build a logo for post object and build 4 images for post object
def new
@post = Post.new
@post.build_logo
4.times do
@post.images.build
end
end
Then you can save the post object with uploaded assets as normal
def create
@post = Post.new(params[:post])
if @post.save
redirect_to post_path(@post)
else
render :action => :new
end
end
Here is the way to display the images for post
<%- @post.images.each do |image| %>
<%= image_tag image.attachment.url(:small) %>
<% end %>
This is a flexible and reusable solution.
