Paperclip :style depending on model (has_many polymorphic images)
I am really late to the party here, but I wanted to clarify something about accessing model data for anyone else that happens on to this thread. I just faced this issue while using Paperclip to apply watermarks based on data from the model, and got it working after a lot of investigation.
You said:
After reading a lot on the Paperclip forum I don't believe it's possible to access the instance before it has been saved. You can only see the Paperclip stuff and that's it.
In fact, you can see the model data if it's been set in the object before your attachment is assigned!
Your paperclip processors and whatnot are invoked when the attachment in your model is assigned. If you rely on mass assignment (or not, actually), as soon as the attachment is assigned a value, paperclip does its thing.
Here's how I solved the problem:
In my model with the attachment (Photo), I made all the attributes EXCEPT the attachment attr_accessible
, thereby keeping the attachment from being assigned during mass assignment.
class Photo < ActiveRecord::Base
attr_accessible :attribution, :latitude, :longitude, :activity_id, :seq_no, :approved, :caption
has_attached_file :picture, ...
...
end
In my controller's create method (for example) I pulled out the picture
from the params
, and then created the object. (It's probably not necessary to remove the picture from params
, since the attr_accessible
statement should prevent picture
from being assgined, but it doesn't hurt). Then I assign the picture
attribute, after all the other attributes of the photo object have been setup.
def create
picture = params[:photo].delete(:picture)
@photo = Photo.new(params[:photo])
@photo.picture = picture
@photo.save
...
end
In my case, one of the styles for picture calls for applying a watermark, which is the text string held in the attribution
attribute. Before I made these code changes, the attribution string was never applied, and in the watermark code, attachment.instance.attribution
was always nil
. The changes summarized here made the entire model available inside the paperclip processors. The key is to assign your attachment attribute last.
Hope this helps someone.
user has many images using paperclip in rails 3
Your problem has nothing to do with paperclip, it's all about polymorphic association. Now, polymorphic association is very similar to regular association - the only difference, looking from the point of view of DB schema, is existence of another column in "belonging" model (in your case, it's imageable_type
), that contains class of associated record. So, firstly, you should remove t.references :image
from create_users
migration, as it's unnecessary there. Secondly, you should add your foreign key column and association type column to create_images
migration. You can do it with single 'directive', like this:
t.references :imageable, polymorphic: true
and this should be enough.
Paperclip add image from URL in has_many association
Here is a great gist (that I did not write). It should get you there: https://gist.github.com/jgv/1502777
require 'open-uri'
class Photo < ActiveRecord::Base
has_attached_file :image # etc...
before_validation :download_remote_image, :if => :image_url_provided?
validates_presence_of :image_remote_url, :if => :image_url_provided?, :message => 'is invalid or inaccessible'
private
def image_url_provided?
!self.image_url.blank?
end
def download_remote_image
io = open(URI.parse(image_url))
self.original_filename = io.base_uri.path.split('/').last
self.image = io
self.image_remote_url = image_url
rescue # catch url errors with validations instead of exceptions (Errno::ENOENT, OpenURI::HTTPError, etc...)
end
end
All credit to the author.
In the future, it's usually best to post the code with your attempt to solve the problem.
paperclip custom path and url for polymorphic associations
You can use Paperclip Interpolations. Interpolations allows you to call a method to determine the value of a part of the path.
class Attachment < ActiveRecord::Base
belongs_to :user
belongs_to :attachable, :polymorphic => true
Paperclip.interpolates :attached_to do |attachment, style|
attachment.instance.attachable.class.to_s.downcase
end
has_attached_file :attachment,
:url => "/attachments/:id/:basename.:extension",
:path => ":rails_root/public/attachments/:attached_to/:id/:basename.:extension",
:default_url => "/attachments/original/no-file.txt"
end
Ruby on Rails - Paperclip and dynamic parameters
I ran into the same Paperclip chicken/egg issue on a project trying to use dynamic styles based on the associated model with a polymorphic relationship. I've adapted my solution to your existing code. An explanation follows:
class Asset < ActiveRecord::Base
attr_accessible :image, :deferred_image
attr_writer :deferred_image
has_attached_file :image,
:styles => lambda { |a| a.instance.styles }
belongs_to :project
after_save :assign_deferred_image
def styles
project.generators.each_with_object({}) { |g, hsh| hsh[g.sym] = "#{g.width}x#{g.height}" }
end
private
def assign_deferred_image
if @deferred_image
self.image = @deferred_image
@deferred_image = nil
save!
end
end
end
Basically, to get around the issue of Paperclip trying to retrieve the dynamic styles before the project relation information has been propagated, you can assign all of the image
attributes to a non-Paperclip attribute (in this instance, I have name it deferred_image
). The after_save
hook assigns the value of @deferred_image
to self.image
, which kicks off all the Paperclip jazz.
Your controller becomes:
# AssetsController
def create
@project = Project.find(params[:project_id])
@asset = @project.assets.build(params[:asset])
@asset.uploaded_by = current_user
respond_to do |format|
# all this is unrelated and can stay the same
end
end
And the view:
<%= form_for @asset do |f| %>
<%# other asset attributes %>
<%= f.label :deferred_upload %>
<%= f.file_field :deferred_upload %>
<%= f.submit %>
<% end %>
This solution also allows using accepts_nested_attributes
for the assets
relation in the Project
model (which is currently how I'm using it - to upload assets as part of creating/editing a Project).
There are some downsides to this approach (ex. validating the Paperclip image
in relation to the validity of the Asset
instance gets tricky), but it's the best I could come up with short of monkey patching Paperclip to somehow defer execution of the style
method until after the association information had been populated.
I'll be keeping an eye on this question to see if anyone has a better solution to this problem!
At the very least, if you choose to keep using your same solution, you can make the following stylistic improvement to your Asset#styles
method:
def styles
(@generators || project.generators).each_with_object({}) { |g, hsh| hsh[g.sym] = "#{g.width}x#{g.height}" }
end
Does the exact same thing as your existing method, but more succinctly.
With paperclip, how can I change the image location to a :parent_model_id/:id folder format?
Ah, I finally figured it out. I needed to use Paperclip.interpolates.
This post from thoughtbot sort of explains it, but it's slightly outdated.
First, create a config/initializers/paperclip.rb file and add the following:
Paperclip.interpolates :listing_id do |attachment, style|
attachment.instance.listing_id # or whatever you've named your User's login/username/etc. attribute
end
Which means that now in my images model I can refer to :listing_id like so:
class Image < ActiveRecord::Base
belongs_to :listing #Rails ActiveRecord Relation. An image belongs to a post.
# paperclip data
has_attached_file :photo, :styles => { :medium => "300x300>", :thumb => "100x100>" },
:url => "/system/:attachment/:listing_id/:id/:style_:filename" #location where to output the server. :LISTING_ID is defined in config/initializers/paperclib.rb
end
PS: You need to restart the server before the changes in initializers.rb take effect.
Amazon s3- Paperclip can't upload images to proper path
I'm not sure, but there is like it usually do:
module Paperclip
module Interpolations
def timestamp attachment, style
attachment.instance_read(:updated_at).to_i
end
end
end
And I think it should looks like:
module Paperclip
module Interpolations
def contributor_id attachment, style
attachment.instance_read(:contributor_id)
end
end
end
You could read more about PaperClip's custom interpolations here
Related Topics
How to Delete Specific Characters from a String in Ruby
How to Write Specs for Code That Depends on Environment Variables
Capistrano & Bash: Ignore Command Exit Status
Vcrproxy: Record Phantomjs Ajax Calls with Vcr Inside Capybara
How to Get an Array with Column Names of a Table
Ruby: What Does the Asterisk in "P *1..10" Mean
Rails Console - Find Where Created at = Certain Day
Is Ruby a Scripting Language or an Interpreted Language
Neither Ruby and Nor Irb Can Load .Rb File in Current Directory
Sorting: Sort Array Based on Multiple Conditions in Ruby
How to Reflect in the Database a New Belongs_To and Has_Many Relationship in Ruby on Rails
Cucumber Not Showing Coloured Output in Windows
Getting a Dns Txt Record in Ruby
Mongodb and Mongoid in Production
Ruby Remove Empty Lines from String
Ruby - Adding a Directory to $Load_Path - What Does It Do
Rendering Partials/View in a Rake Task/Background Job/Model in Rails 4