Rails和回形针,删除记录但不删除附件

时间:2011-05-16 07:56:23

标签: ruby-on-rails ruby image-processing callback paperclip

我使用rails和paperclip来保存图像,这是常用的方式。

当销毁附件的记录时,附件也会从文件系统中删除。

99%的时间这是正确的操作,但是有一种情况我需要附件保留在系统中,即使删除了db记录。

我想知道是否有人知道如何做到这一点。

在销毁记录之前,我尝试通过update_attribute将附件字段设置为nil,但update_attribute也会删除该文件。

一种方法是忽略所有的回调,但是需要一些其他回调,这看起来有点太多了。任何人都知道更好的方式......

干杯。

6 个答案:

答案 0 :(得分:3)

您可能想看看在回形针中如何实现Attachment#assign(在object.attachment = new_attachment时调用)。 基本上,它会进行一些设置,然后调用Attachment#clear,然后保存新文件。

附件#clear将旧文件放入再次调用save时处理的删除队列中,你想要的只是避免调用clear,你可以通过编写一个新的assign方法来跳过该行或者通过猴子修补#clear以使其成为无操作。从理论上讲,你可以在想要发生这种情况的实例上修补它,但在我看来,你可能想要为整个项目做这件事。

或者您可以清除保存处理队列的实例变量。该变量没有访问器,但执行instance_variable_get

应该是微不足道的

答案 1 :(得分:1)

禁止paperclip生成的数据库字段(file_name,content_type,file_size)不保留文件。 destroy方法仍会指向它通过索引。

在销毁记录之前,尝试将id更改为某个随机数(例如999898)。如果它抛出异常,也会使字段为零。这样,记录将不再指向文件,并将在记录被销毁时保留。

答案 2 :(得分:1)

所以问题是发生了什么?您正在尝试实施撤消,以便有人可以删除然后取消删除?

我认为你的解决方案应该处理一个“清除”标志,然后如果clear标志为真,则每晚在批处理作业中删除。只要所有获取都是针对带有索引的clear = false的记录,就不会影响性能。这个问题在解决方案中只是有一种“错误”的感觉。只是提供不同的视角。

答案 3 :(得分:1)

可能有点偏离主题:

如果附件存储在S3上 - 您可以覆盖has_attached_file方法并仅传递附件名称而不使用任何选项。在这种情况下,Paperclip会认为文件存储在文件系统中并且不会删除任何内容。也没有例外。

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

答案 4 :(得分:1)

gem允许您进行软删除:

(来自paperclip

软删除文件保存

有一个选项可用于保存附件,以便与软删除模型很好地配合使用。 (acts_as_paranoid,偏执狂等)

has_attached_file :some_attachment, {
    :preserve_files => "true",
}

这样可以防止在模型被破坏时消除some_attachment,因此在稍后恢复对象时它仍然存在。

答案 5 :(得分:1)

对我来说,重写Paperclip::Attachment#clear方法并不能解决问题。我不得不覆盖Paperclip::Attachment#queue_all_for_delete

正如Alex Falke所说,Paperclip有:preserve_files选项,所以很明显,如果你想保留所有附件,你会使用它而不是覆盖。

如果您有特殊情况,请覆盖#queue_all_for_delete,但您必须选择性地执行此操作。猴子修补是全球性的,因此它不是最好的方法。我试图使用改进来限制猴子修补范围,但词法范围并没有很好地适应我的用例。

所以我最终得到了这个模块,它将自身注入到方法查找路径中,并有选择地定义或取消定义#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