Rails和paperclip,删除记录但不删除附件。

6
我正在使用Rails和Paperclip保存图片,这是通常的方式。
当带有附件的记录被删除时,文件系统中的附件也会被删除。
99%的时间,这是正确的操作,但是有一种情况,即使删除了数据库记录,我仍需要附件保留在系统中。
我想知道是否有人知道如何做到这一点。
我尝试在销毁记录之前通过update_attribute将附件字段设置为nil,但是update_attribute也会删除文件。
一种方法是忽略所有回调,但其他回调是必需的,这似乎有点过头了。 有没有更好的方法...
干杯。
6个回答

3
您可能想看一下paperclip中如何实现Attachment#assign(当您执行object.attachment = new_attachment时调用)。基本上,它进行了一些设置,然后调用Attachment#clear,然后保存新文件。 Attachment#clear会将旧文件放入一个删除队列中,在再次调用save时处理该队列,您要做的就是避免调用clear。您可以编写一个新的assign方法来跳过该行,或者通过monkey patching #clear使其成为no-op。理论上,您可以在需要此功能的实例上直接进行monkey patching,但我认为您可能希望为整个项目执行此操作。
或者您可以清除保存处理队列的实例变量。该变量没有访问器,但使用instance_variable_get应该很容易。

3

这个gem可以让你进行软删除:

(来自于paperclip)

软删除的文件保存

为了与软删除模型(acts_as_paranoid, paranoia等)兼容,提供了一个选项以保留附件。

has_attached_file :some_attachment, {
    preserve_files: true,
}

当模型被销毁时,这将防止some_attachment被清除,因此当对象稍后恢复时,它仍将存在。


True不应该是一个字符串。 - yekta
fixed! Thanks @yekta - Alex Falke

1

将Paperclip生成的数据库字段(file_name、content_type、file_size)设置为nil不会保留文件。destroy方法仍然会通过索引指向它。

在销毁记录之前,尝试将ID更改为某个随机数字(例如999898)。如果抛出异常,也将字段设置为nil。这样,记录将不再指向该文件,并且在记录被销毁时仍将保留。


1
所以问题是发生了什么?你正在尝试实现撤销,以便某人可以删除然后取消删除?
我认为你的解决方案应该处理一个“清除”标志,然后在夜间批量作业中删除,如果清除标志为真。只要所有获取都是针对清除=false的记录并带有索引,就不会影响性能。这个问题只是在解决方案上有一种“错误”的感觉。只是提供一个不同的视角。

1

也许有点离题:

如果附件存储在S3上 - 您可以覆盖has_attached_file方法,并仅传递附件名称而不带任何选项。在这种情况下,Paperclip将认为文件存储在文件系统中,因此不会删除任何内容,也不会引发异常。

我知道这个解决方案可能有点hacky/ugly,但它很简单并且有效。


1
对我来说,覆盖Paperclip :: Attachment#clear方法并没有起作用。 我不得不覆盖Paperclip :: Attachment#queue_all_for_delete
正如Alex Falke所说,Paperclip有:preserve_files 选项,因此如果您想保留所有附件,则应使用它而不是覆盖。
如果您有特殊情况,则覆盖#queue_all_for_delete 是正确的方法,但您必须有选择地这样做。 Monkey patching是全局的,因此不是最好的方法。 我尝试使用refinements来限制monkey patching范围,但词法作用域与我的用例不兼容。
所以我最终使用了这个模块,它将自己注入到方法查找路径中,并有选择地定义或取消定义#queue_all_for_delete 的无操作覆盖:
module PaperclipKeepAttachment
  def self.with_patch
    patch
    add_override
    yield
  ensure
    remove_override
  end

  def self.patch
    me = self
    Paperclip::Attachment.class_eval{ prepend me } unless Paperclip::Attachment.ancestors.include?(me)
  end

  def self.add_override
    define_method(:queue_all_for_delete) do
      # no-op
    end
  end

  def self.remove_override
    remove_method(:queue_all_for_delete)
  end
end

使用它,您可以做出类似以下的操作:
PaperclipKeepAttachment.with_patch do 
  # The record will be destroyed but the attachment kept intact
  first_record.destroy
end

# The record will be destroyed and the attachment removed
second_record.destroy

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接