回形针:样式取决于模型(has_many多态图像)

时间:2010-10-28 14:04:08

标签: ruby-on-rails ruby paperclip

我已将模型设置为使用多态Image模型。这工作正常,但我想知道是否可以更改每个模型的:样式设置。使用STI(Model< Image)找到了一些例子但是这对我来说不是一个选项,因为我使用的是has_many关系。

技术

has_many :images, :as => :imageable

图像

belongs_to :imageable, :polymorphic => true
has_attached_file :file, :styles => { :thumb => "150x150>", :normal => "492x600>"}
                         #Change this setting depending on model

更新

我尝试在Proc方法中启动调试器。仅填充与附加文件相关的字段:

run'irb(Image):006:0> a.instance => #<Image id: nil, created_at: nil, updated_at: nil, imageable_id: nil, imageable_type: nil, file_file_name: "IMG_9834.JPG", file_content_type: "image/jpeg", file_file_size: 151326, file_updated_at: "2010-10-30 08:40:23">

这是来自ImageController #create

的对象
ImageController#create
@image => #<Image id: nil, created_at: nil, updated_at: nil, imageable_id: 83, imageable_type: "Art", file_file_name: "IMG_9834.JPG", file_content_type: "image/jpeg", file_file_size: 151326, file_updated_at: "2010-10-30 08:32:49">

我正在使用paperclip(2.3.5)和Rails 3.0.1。无论我做什么,a.instance对象都是只有与填充的附件相关的字段的图像。有什么想法吗?

UPDATE2

在Paperclip论坛上阅读了很多内容后,我认为在保存实例之前无法访问该实例。你只能看到Paperclip的东西,就是这样。

我通过使用前置过滤器从图像控制器中预设图像来解决这个问题 - 没有附件

  before_filter :presave_image, :only => :create

  ...

  private

  def presave_image
    if @image.id.nil? # Save if new record / Arts controller sets @image
      @image = Image.new(:imageable_type => params[:image][:imageable_type], :imageable_id => params[:image][:imageable_id])
      @image.save(:validate => false)
      @image.file = params[:file] # Set to params[:image][:file] if you edit an image.
    end
  end

10 个答案:

答案 0 :(得分:8)

我只是在使用Paperclip根据模型中的数据应用水印时遇到了这个问题,经过大量调查后才开始工作。

你说:

  

在Paperclip论坛上阅读了很多内容后,我认为在保存实例之前无法访问该实例。你只能看到Paperclip的东西,就是这样。

事实上,如果在分配附件之前在对象中设置了模型数据,则可以看到模型数据

您的回形针处理器以及在分配模型中的附件时调用的内容。如果您依赖于质量分配(或实际上不是),只要为附件分配了一个值,回形针就会发挥作用。

以下是我解决问题的方法:

在带有附件(照片)的模型中,除了附件attr_accessible之外,我创建了所有属性,从而在批量分配期间保留附件。

class Photo < ActiveRecord::Base
  attr_accessible :attribution, :latitude, :longitude, :activity_id, :seq_no, :approved, :caption

  has_attached_file :picture, ...
  ...
end

在我的控制器的create方法中(例如),我从picture中取出了params,然后创建了该对象。 (可能没有必要从params 删除图片,因为attr_accessible语句应该阻止picture被分配,但它不会受到伤害。 然后我分配了picture属性,之后设置了照片对象的所有其他属性。

def create
  picture = params[:photo].delete(:picture)
  @photo = Photo.new(params[:photo])
  @photo.picture = picture

  @photo.save
  ...
end

在我的例子中,用于应用水印的图片调用样式之一,即attribution属性中保存的文本字符串。在我更改代码之前,从未应用归因字符串,而在水印代码中,attachment.instance.attribution始终为nil。这里总结的变化使得整个模型在回形针处理器内可用。 关键是分配您的附件属性 last

希望这有助于某人。

答案 1 :(得分:5)

我找到了在创建时拾取样式的解决方法。 关键是要实现before_post_processafter_save挂钩。

class Image < ActiveRecord::Base

  DEFAULT_STYLES = {
    medium: "300x300>", thumb: "100x100>"
  }

  has_attached_file :file, styles: ->(file){ file.instance.styles }


  validates_attachment_content_type :file, :content_type => /\Aimage\/.*\Z/

  # Workaround to pickup styles from imageable model
  # paperclip starts processing before all attributes are in the model
  # so we start processing after saving

  before_post_process ->{
    !@file_reprocessed.nil?
  }

  after_save ->{
    if !@file_reprocessed && (file_updated_at_changed? || imageable_type_changed?)
      @file_reprocessed = true
      file.reprocess!
    end
  }


  belongs_to :imageable, polymorphic: true

  def styles
    if imageable_class.respond_to?(:image_styles)
      imageable_class.image_styles
    end || DEFAULT_STYLES
  end

  def imageable_class
    imageable_type.constantize if imageable_type.present?
  end


end

所以你必须在imageable_class中定义image_styles类方法 就我而言,它是

class Property < ActiveRecord::Base

  def self.image_styles
    {
      large: "570x380#",
      thumb: "50x70#",
      medium: "300x200#"
    }
  end

end

答案 2 :(得分:3)

:styles属性以Proc为参数,因此你可以做各种奇特的事情:)

class Image < AR::Base
  has_attached_file :file, :styles => Proc.new { |a| a.instance.file_styles }

  def file_styles; { :thumb => "150x150>", :normal => "492x600>" } end
end

class Didum < Image
  def file_styles; { :thumb => "50x50>", :normal => "492x600>" } end
end

注意 - 上面的代码应该有效,但老实说我没有设置来验证它,但看起来Paperclip::Attachment#styles call如果它响应它,请参阅http://rdoc.info/github/thoughtbot/paperclip/master/Paperclip/Attachment:styles

更新传递给Proc的对象不是实例,而是Paperclip::Attachment,但实例可通过附件instance访问

PS:我在其他一些地方看过这个,但不记得在哪里......

答案 3 :(得分:1)

class Banner < ActiveRecord::Base  

  belongs_to :banner_categoria
  validates :banner_categoria_id ,{:presence =>{:message => "não informada"}} 

  has_attached_file :arquivo
  after_initialize :init_attachment




  def init_attachment
    self.class.has_attached_file :arquivo,
      :url => "/system/:class/:attachment/:id/:style/:basename.:extension",
      :path => ":rails_root/public/system/:class/:attachment/:id/:style/:basename.:extension",
      :styles => hash = {
      :banner => {
        :geometry => "#{self.banner_categoria.largura}x#{self.banner_categoria.altura}>",
        :quality => 80
      },
      :thumb => "100x100#"
    }
  end

答案 4 :(得分:1)

我喜欢MarkGranoff的答案,但我提出了一个更简单的实现方法,可以帮助我,并且看起来更易于维护。

#create new and instantiate the fields you need ( or all except the attachment )
@photo = Photo.new(:attribution => params[:photo][:attribution],
                   :latitude => params[:photo][:latitude ])

#then just assign all params as normal
@photo = Photo.assign_attributes(params[:node])

第二个语句分配附件以便处理器被触发,并且由于您已经分配了:attribution和:latitude,它们的值将通过attachment.instance方法在处理器中可用。

感谢大家的见解!

答案 5 :(得分:1)

我也遇到了同样的问题,在寻找优雅的解决方案后,我没有找到任何“真正”有用的东西。所以,这是我的简单解决方案,现在似乎没问题; (此时我不确定它是否有任何缺点)

在模特中;

class Asset < ActiveRecord::Base
  belongs_to :assetable, polymorphic: true

  has_attached_file :attachment,  path: ":rails_root/#{path}/assets/images/:style/:filename",
                                  url: '/assets/images/:style/:filename',
                                  styles: -> (a) { a.instance.send(:styles) }
private
  def styles
    raise 'Undefined assetable.' unless assetable

    if assetable.class == Photo
      { small: 'x40', medium: '120x', large: '300x' }
    elsif assetable.class == Avatar
      { small: 'x40', medium: '120x', large: '300x' }
    else
      raise "Styles for #{assetable.class} is not defined."
    end
  end
end

在控制器中;

@photo = Photo.new
@photo.build_image(assetable: @photo, attachment: params[:photo][:image_attributes][:attachment])
@photo.save

答案 6 :(得分:1)

经过几个小时深入研究ActiveRecordPaperclip的源代码,我认为这是一个涉及一些猴子修补hackery的解决方案。

我还没有彻底测试它,但似乎能满足我的谦逊需求。

以下是我的config/initializers/activerecord_associations_patch.rb

module ActiveRecord
  module Associations
    class HasManyAssociation

      def build_record(attributes)
        if options[:as] && owner
          # Unicorns
          reflection.build_association({}) do |record|
            set_owner_attributes(record)
            unless foreign_key_for?(record)
              record.public_send "#{options[:as]}=", owner
            end
            initialize_attributes(record)
            record.assign_attributes(attributes)
          end
        else
          # Classic Rails way
          reflection.build_association(attributes) do |record|
            initialize_attributes(record)
          end
        end
      end

    end
  end
end

由于reflection不知道我们的@owner,它首先会分配attributes来触发对record.#{instance}=的调用(即Image#file=),然后将其转发到#assign并执行post_processing挂钩,这最终会调用styles中提供的Proc has_attached_file而不会出现多态imageable被设定。嗯...

我重写了首先构建记录的方法,然后再分配提供的属性。 此外,它在record#new_record?支票中占foreign_key_for?

这样,一旦正确设置了记录,就会分配attributes。至少我是这么认为的;)

非常欢迎更清洁的解决方案和建设性意见:)

答案 7 :(得分:0)

在处理模型上的动态行为变化时,我遇到了类似的问题。玩弄irb,我发现这有效:

module Foo
   attr_accessor :bar
end
class Bar
   extends Foo
end
bar.bar = 'test' # 'test'
bar.bar # 'test'
# also works for instances of Bar!

因此,我将创建一个名为image_style的属性,可以通过在图像初始化上使用此代码将其更改为您要添加的任何模块:

  def after_initialize
    if self.image_style?
       extend Object.const_get(image_style)
    else
       extend DefaultImageStyle
    end
  end

我只是想知道这是否适用于paperclip,因为after_initialize方法可能会在回形针之后调用它的魔力。值得尝试一下!

答案 8 :(得分:0)

查看附件的Paperclip源,看起来styles哈希可以采用响应call的对象,因此您可以执行以下操作:

class Image < ActiveRecord::Base
  belongs_to :imageable, :polymorphic => true
  has_attached_file :file, :styles => lambda {|attachment| attachment.instance.imageable_type.constantize.image_styles }
end

然后在任何有许多图像的模型中:

class Art < ActiveRecord::Base
  has_many :images, :as => :imageable

  def self.image_styles
    { :thumb => "150x150>", :normal => "492x600>" }
  end
end

修改:看起来有人打败了我:P。

答案 9 :(得分:-2)

在生产/登台服务器中遇到同样的问题...但不在我的本地环境中。我在所有服务器(2.3.2和2.2.6)中使用相同的rails / paperclip版本