Reputation: 3433
Goal: One Rails model (table) with multiple models which inherit from it, each which define their own paperclip has_attached_file
configurations.
I used to have a single Upload class which I was using with paperclip. The problem is that as I added more file types (pdfs, word documents etc.) they were still being processed as images via the "style" and "convert_options". Further, now I need to have some files stored on S3 and others stored locally.
That said, I've been restructuring things such that I now have an S3File, Contract and other models all which inherit from Upload which still inherits from ActiveRecord::Base.
# app/models/project.rb
class Project < ActiveRecord::Base
has_many :contracts, :dependent => :destroy
accepts_nested_attributes_for :contracts, :allow_destroy => true
has_many :s3_files, :dependent => :destroy
accepts_nested_attributes_for :s3_files, :allow_destroy => true
# ...
end
# app/models/upload.rb
class Upload < ActiveRecord::Base
belongs_to :project
end
# app/models/contract.rb
class Contract < Upload
has_attached_file :item,
:url => "/projects/:project_id/uploads/:id/:basename.:extension",
:path => ":rails_root/public/:class/:attachment/:id/:basename.:extension"
do_not_validate_attachment_file_type :item
end
# app/models/s3_file.rb
class S3File < Upload
has_attached_file :s3file,
storage: :s3,
url: ':s3_domain_url',
path: lambda {|u| 'files/:basename.:extension' }
do_not_validate_attachment_file_type :s3file
end
Now in a console when I try and query the data, it returns Upload objects and not the S3File or Contract.
irb(main):005:0> Project.first.contracts
=> #<Upload id: 14833, project_id: 9717, upload_type: "private", item_file_name: "abcd.pdf", item_category: "contracts", item_content_type: "application/pdf", item_file_size: 671367, rake_processed: 0, name: "", created_at: "2013-05-30 20:05:02", updated_at: "2013-05-30 20:05:02">
Having the response as an Upload type is problematic because it has no paperclip attachment. These are defined on the subclasses which each have a unique url, path and storage definitions for the has_attached_file
.
I saw the multiple models and paperclip and polymorphic multiple models paperclip questions but in those questions each of the "multiple models" inherit from ActiveRecord::Base, my goal is to avoid that if I can since the data structures are the same.
My questions about this are:
has_attached_file
(:item or :s3file) names or is it beneficial to have them remain the same?Upvotes: 4
Views: 772
Reputation: 3433
STI with paperclip has been discussed in several issues #293, #601, #605 and is a supported feature of paperclip now.
Is STI the right approach?
It was the right approach in my case since I didn't want to use multiple tables for each model. In order to solve the problem of having the wrong class when querying project.contracts
and getting Upload
type classes, this is a documented Rails feature. Simply adding class_name: "Contract
to the has_many on your model will address this:
Per the associations documentation on customizing the query:
Customizing the query
Associations are built from Relations, and you can use the Relation syntax to customize them. For example, to add a condition:
class Blog < ActiveRecord::Base
has_many :published_posts, -> { where published: true }, class_name: 'Post'
end
Are polymorphic associations necessary
In this case with STI, no. If each model was db backed then yes a polymorphic association could help. See the answer on polymorphic associations with multiple models for an example.
Is it a good idea to use the same has_attached_file
Ever since the aforementioned pull request has been merged this is a non-issue in paperclip. One can overwrite the options, callbacks, styles, url or path in a subclass and enjoy the benefits of OOP to suit the needs of the application.
Upvotes: 2