Paperclip :Style Depending on Model (Has_Many Polymorphic Images)

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



Leave a reply



Submit