Скрепка: стиль в зависимости от модели (has_many полиморфные изображения)
Я установил свои модели для использования полиморфной модели изображения. Это прекрасно работает, однако мне интересно, можно ли изменить настройку стилей для каждой модели. Нашли несколько примеров, используя 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
UPDATE
Я попытался запустить отладчик внутри метода 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, я не считаю возможным доступ к экземпляру до его сохранения. Вы можете видеть только материал скрепки и что это.
Я столкнулся с этой проблемой, предварительно создав изображение с контроллера Image с фильтром до фильтра - без вложения
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
Ответы
Ответ 1
Я очень опаздываю на вечеринку здесь, но я хотел кое-что уточнить о доступе к данным модели для всех, кто происходит с этим потоком. Я просто столкнулся с этой проблемой при использовании Paperclip для применения водяных знаков на основе данных модели и заработал после многих исследований.
Ты сказал:
Прочитав много на форуме Paperclip, я не считаю возможным доступ к экземпляру до его сохранения. Вы можете видеть только материал скрепки и что это.
Фактически вы можете увидеть данные модели, если они были установлены в объекте до назначения вашего вложения!
Ваши процессоры скрепки и многое другое вызывают при назначении вложения в вашей модели. Если вы полагаетесь на массовое присвоение (или нет, на самом деле), как только вложение присваивается значение, скрепка делает свою вещь.
Вот как я решил проблему:
В моей модели с приложением (Фото) я выполнил все атрибуты ЗА ИСКЛЮЧЕНИЕМ вложения attr_accessible
, тем самым сохраняя привязку к назначению во время массового присваивания.
class Photo < ActiveRecord::Base
attr_accessible :attribution, :latitude, :longitude, :activity_id, :seq_no, :approved, :caption
has_attached_file :picture, ...
...
end
В моем методе создания контроллера (например) я вытащил 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
. Ниже приведенные изменения сделали всю модель доступной внутри процессоров скрепки. Ключ должен назначить последний атрибут вложений.
Надеюсь, это поможет кому-то.
Ответ 2
Я нашел способ обхода стилей при создании.
Ключ состоит в том, чтобы реализовать крюки before_post_process
и after_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 ->{
[email protected]_reprocessed.nil?
}
after_save ->{
if [email protected]_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
Итак, вы должны определить метод класса image_styles в imageable_class
В моем случае это было
class Property < ActiveRecord::Base
def self.image_styles
{
large: "570x380#",
thumb: "50x70#",
medium: "300x200#"
}
end
end
Ответ 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
does call
, если он отвечает на него, см. http://rdoc.info/github/thoughtbot/paperclip/master/Paperclip/Attachment:styles
UPDATE объект, переданный в Proc
, не является экземпляром, а Paperclip::Attachment
, но экземпляр доступен через instance
на вложении
PS: И я видел это в некоторых других местах, но не могу вспомнить, где...
Ответ 4
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
конец
Ответ 5
Мне понравился ответ 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])
Второй оператор назначает вложение, поэтому процессор запускается, и поскольку вы уже назначили: атрибуция и: широта, их значения будут доступны в процессоре с помощью метода attachment.instance.
Спасибо всем за понимание!
Ответ 6
Я также встретился с той же проблемой, и после поиска элегантного решения я не нашел ничего "действительно" полезного. Итак, вот мое простое решение, которое сейчас выглядит хорошо; (я не уверен, есть ли у него какие-то недостатки на данный момент)
В модели;
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
Ответ 7
После нескольких часов работы в исходном коде обоих, ActiveRecord
и Paperclip
, я думаю, что есть решение, связанное с небольшим количеством хакерских атак с обезьяной.
Я не тщательно его протестировал, но, похоже, работает для моих скромных потребностей.
Ниже мой 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
hook, который в итоге приводит к вызову styles
Proc
, предоставленному в has_attached_file
, если не установлен полиморфный imageable
. да...
Я переписал метод, чтобы сначала создать запись и назначить предоставленные атрибуты позже.
Кроме того, он учитывает record#new_record?
в foreign_key_for?
check.
Таким образом attributes
назначаются после правильной настройки записи. По крайней мере, я так думаю;)
Более удобные решения и конструктивные комментарии приветствуются:)
Ответ 8
У меня был аналогичный вопрос, касающийся динамических поведенческих изменений на моих моделях. Играя с 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 может вызываться после того, как paperclip делает это волшебство. Стоит попробовать, хотя!
Ответ 9
Глядя на источник скрепки для прикрепления, похоже, хэш 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.
Ответ 10
имеют ту же проблему на сервере производства/промежуточного уровня... НО не в моей локальной среде. Я использую те же версии рельсов/скрепки на всех серверах (2.3.2 и 2.2.6)