9

我有一个同时使用的模型:用于存储照片和PaperTrail版本控制的 Carrierwave。

我还配置了 Carrierwave 以在更新时存储不同的文件(那是因为我想对照片进行版本控制)config.remove_previously_stored_files_after_update = false

问题是PaperTrail尝试从照片(CarrierWave Uploader)存储整个 Ruby 对象,而不是简单地存储一个字符串(这将是它的 url)

(版本表、列对象)

---
first_name: Foo
last_name: Bar
photo: !ruby/object:PhotoUploader
  model: !ruby/object:Bla
    attributes:
      id: 2
      first_name: Foo1
      segundo_nombre: 'Bar1'
      ........

如何解决此问题以在照片版本中存储一个简单的字符串?

4

6 回答 6

10

您可以覆盖item_before_change您的版本化模型,这样您就不会直接调用上传器访问者write_attribute而是使用。或者,由于您可能希望对多个模型执行此操作,您可以直接对方法进行猴子修补,如下所示:

module PaperTrail
  module Model
    module InstanceMethods
      private
        def item_before_change
          previous = self.dup
          # `dup` clears timestamps so we add them back.
          all_timestamp_attributes.each do |column|
            previous[column] = send(column) if respond_to?(column) && !send(column).nil?
          end
          previous.tap do |prev|
            prev.id = id
            changed_attributes.each do |attr, before|
              if defined?(CarrierWave::Uploader::Base) && before.is_a?(CarrierWave::Uploader::Base)
                prev.send(:write_attribute, attr, before.url && File.basename(before.url))
              else
                prev[attr] = before
              end
            end
          end
        end
    end
  end
end

不确定这是否是最好的解决方案,但它似乎有效。

于 2012-02-25T00:47:10.333 回答
6

添加@beardedd 的评论作为答案,因为我认为这是解决问题的更好方法。

将您的数据库列命名为picture_filename,然后在您的模型中使用以下命令安装上传器:

class User < ActiveRecord::Base has_paper_trail mount_uploader :picture, PictureUploader, mount_on: :picture_filename end

您仍然使用该user.picture.url属性来访问您的模型,但 PaperTrail 会将修订存储在picture_filename.

于 2015-02-25T07:52:23.523 回答
2

这是来自@rabusmar 的monkeypatch 的一些更新版本,我将它用于rails 4.2.0 和paper_trail 4.0.0.beta2,在/config/initializers/paper_trail.rb.

object_changes如果您对版本使用可选列,则需要第二种方法覆盖。如果您在上传程序中覆盖,它对carrierwave +雾的工作方式有点奇怪filename,旧值将来自云,新值来自本地文件名,但在我的情况下没关系。

另外,当您恢复旧版本时,我还没有检查它是否正常工作。

module PaperTrail
  module Model
    module InstanceMethods
      private

      # override to keep only basename for carrierwave attributes in object hash
      def item_before_change
        previous = self.dup
        # `dup` clears timestamps so we add them back.
        all_timestamp_attributes.each do |column|
          if self.class.column_names.include?(column.to_s) and not send("#{column}_was").nil?
            previous[column] = send("#{column}_was")
          end
        end
        enums = previous.respond_to?(:defined_enums) ? previous.defined_enums : {}
        previous.tap do |prev|
          prev.id = id # `dup` clears the `id` so we add that back
          changed_attributes.select { |k,v| self.class.column_names.include?(k) }.each do |attr, before|
            if defined?(CarrierWave::Uploader::Base) && before.is_a?(CarrierWave::Uploader::Base)
              prev.send(:write_attribute, attr, before.url && File.basename(before.url))
            else
              before = enums[attr][before] if enums[attr]
              prev[attr] = before
            end
          end
        end
      end

      # override to keep only basename for carrierwave attributes in object_changes hash
      def changes_for_paper_trail
        _changes = changes.delete_if { |k,v| !notably_changed.include?(k) }
        if PaperTrail.serialized_attributes?
          self.class.serialize_attribute_changes(_changes)
        end
        if defined?(CarrierWave::Uploader::Base)
          Hash[
              _changes.to_hash.map do |k, values|
                [k, values.map { |value| value.is_a?(CarrierWave::Uploader::Base) ? value.url && File.basename(value.url) : value }]
              end
          ]
        else
          _changes.to_hash
        end
      end

    end
  end
end
于 2015-04-07T12:19:11.883 回答
1

这对我来说实际上是有用的,把它放在 config/initializers/paper_trail/.rb

module PaperTrail
  module Reifier
    class << self
      def reify_attributes(model, version, attrs)
        enums = model.class.respond_to?(:defined_enums) ? model.class.defined_enums : {}
        AttributeSerializers::ObjectAttribute.new(model.class).deserialize(attrs)
        attrs.each do |k, v|

          is_enum_without_type_caster = ::ActiveRecord::VERSION::MAJOR < 5 && enums.key?(k)

          if model.send("#{k}").is_a?(CarrierWave::Uploader::Base)
            if v.present?
               model.send("remote_#{k}_url=", v["#{k}"][:url])
               model.send("#{k}").recreate_versions!
            else
               model.send("remove_#{k}!")
            end
          else
              if model.has_attribute?(k) && !is_enum_without_type_caster
                model[k.to_sym] = v
              elsif model.respond_to?("#{k}=")
                model.send("#{k}=", v)
              elsif version.logger
                version.logger.warn(
                  "Attribute #{k} does not exist on #{version.item_type} (Version id: #{version.id})."
                )
              end
            end
        end
      end
    end
  end
end

这会覆盖 reify 方法以在 S3 + heroku 上工作

为了让上传者保留旧文件不被更新或删除的记录,请在上传者中执行此操作

configure do |config|
   config.remove_previously_stored_files_after_update = false
end
def remove!
   true
end

然后编一些套路不定时清理旧文件,祝你好运

于 2016-12-20T20:55:28.670 回答
0

我想在前面的答案中添加以下内容:

您可能会上传具有相同名称的不同文件,这可能会覆盖您以前的文件,因此您将无法恢复旧文件。

您可以在文件名中使用时间戳为所有版本化文件创建随机且唯一的文件名

更新

当在单个请求请求中将多个文件分配给同一个对象时,这似乎不适用于我的所有边缘情况。

我现在正在使用这个:

def filename
  [@cache_id, original_filename].join('-') if original_filename.present?
end

这似乎有效,因为@cache_id每次上传都会再次生成 (情况并非如此,因为上面链接中提供的想法似乎并非如此)。

于 2015-04-11T21:41:09.580 回答
0

@Sjors 教务长

我们还需要覆盖PaperTrail ::Model::InstanceMethods模块中的 pt_recordable_object 方法

  def pt_recordable_object
    attr = attributes_before_change
    object_attrs = object_attrs_for_paper_trail(attr)

    hash = Hash[
        object_attrs.to_hash.map do |k, value|
          [k, value.is_a?(CarrierWave::Uploader::Base) ? value.url && File.basename(value.url) : value ]
        end
    ]

    if self.class.paper_trail_version_class.object_col_is_json?
      hash
    else
      PaperTrail.serializer.dump(hash)
    end
  end
于 2016-02-11T08:16:16.790 回答