【问题标题】:Rails, Attachment_fu - deep copy of database storage attachmentsRails, Attachment_fu - 数据库存储附件的深拷贝
【发布时间】:2011-01-13 09:21:09
【问题描述】:

我有一个模型,比如说附件,它使用 attachment_fu 来接受用户上传的文件。我想“深度复制”(或在 Ruby 语言中,深度克隆)一个附件,从而在“db_files”表中创建一个全新的二进制对象。

我发现这还不是一个完全解决的问题。这篇博文: http://www.williambharding.com/blog/rails/rails-faster-clonecopy-of-attachment_fu-images/

展示了一种据称适用于基于文件系统的存储的方法。对于基于数据库的存储,“深拷贝”失败。创建了一个新的“附件”,但它使用了预先存在的 db_file_id,因此执行了浅拷贝。

在 attachment_fu 的 db_file_backend.rb 里面我看到了保存方法:

      # Saves the data to the DbFile model
      def save_to_storage
        if save_attachment?
          (db_file || build_db_file).data = temp_data
          db_file.save!
          self.class.update_all ['db_file_id = ?', self.db_file_id = db_file.id], ['id = ?', id]
        end
        true
      end

所以,我试图破译这一点,我相信“build_db_file”是 DbFile.new 的一些 Ruby 元编程魔法简写,尽管我无法确认这一点(grepping 源显示没有提及这一点,我也无法在谷歌上找到它) .

我不太确定它在做什么,但我的理论是,作为“深拷贝”尝试的一部分(在链接代码中),正在从源 obj 复制 db_file,因此它只是触发了保存而不是创建。

我最初的理论是父(附件)对象会在尝试进行深度复制时被设置为“新”,因此我做了类似的事情:

 def save_to_storage
    if save_attachment?
      if self.new_record?
        db_file = DbFile.new :data => temp_data
        self.class.update_all ['db_file_id = ?', self.db_file_id = db_file.id], ['id = ?', id]
      end
    end
    true
  end

这实际上适用于克隆对象,但不幸的是,所有常规、非克隆文件上传的测试都失败了。附件对象已创建,但没有数据写入 db_file。理论上是先保存父对象,然后再写入db_file的东西,从而new_record?返回 false。

所以,作为一个实验,我决定尝试:

  def save_to_storage
    if save_attachment?
      if self.new_record?
        db_file = DbFile.new :data => temp_data
        self.class.update_all ['db_file_id = ?', self.db_file_id = db_file.id], ['id = ?', id]
      else
        (db_file || build_db_file).data = temp_data
        db_file.save!
        self.class.update_all ['db_file_id = ?', self.db_file_id = db_file.id], ['id = ?', id]
      #end
    end
    true
  end

这部分有效 - db_file 已填充,但随后我在 db_file.save 上收到错误消息! - 说 db_file 是 nil。

所以,我有点受阻。我可以做一些进一步的试验和错误,但在这一点上,我对这个插件如何工作的理解有限。我真的没想到也不想花这么多时间在上面,所以我不愿意进一步探索attachment_fu,但恐怕我将不得不深入兔子洞才能弄清楚。有什么想法或想法吗?

谢谢!!

【问题讨论】:

  • 注意:更优雅的解决方案是使用引用计数的形式,并让所有附件 obj 指向相同的 db_file_id。只有在没有指向它的附件之后才删除 db_file。嗯

标签: ruby-on-rails ruby deep-copy attachment-fu


【解决方案1】:

这只是解释build_db_file 调用的部分回复

正如您所怀疑的,build_db_file 调用执行了一个方法 generated by creating a belongs_to association。关联在此处创建:

def self.included(base) #:nodoc:
   Object.const_set(:DbFile, Class.new(ActiveRecord::Base)) unless Object.const_defined?(:DbFile)
   base.belongs_to  :db_file, :class_name => '::DbFile', :foreign_key => 'db_file_id'
end

所以(db_file || build_db_file) 语句采用现有的关联DbFile 对象,如果它为nil,则创建一个新对象,并将temp_data 分配给其二进制字段datatemp_data 可能是包含表单数据的字节数组。

我有一个问题(我不能对你的问题发表评论)——你为什么不在创建 db_file.save! 之后打电话

db_file = DbFile.new :data => temp_data

?

【讨论】:

  • 好问题。这是我的一个错误,虽然它不能解决问题:) 谢谢!
【解决方案2】:

好的,所以我没有弄清楚如何创建一个新的 db_file(这在我们的特定情况下是浪费的),我只是对 destroy_file 进行了猴子修补,以仅在没有更多附件记录指向它的情况下删除 db_file。如果您允许某人原位“修改”附件 db_file,这可能不合适,但由于我们不这样做,因此效果很好。

Technoweenie::AttachmentFu::Backends::DbFileBackend.module_eval do
  protected
  def destroy_file
    if db_file && self.class.count( :conditions =>["id <> ? AND db_file_id = ?", self.id, db_file.id] ) == 0
      db_file.destroy 
    end
  end
end

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-06-27
    • 1970-01-01
    • 2012-04-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-01-13
    • 2011-09-05
    相关资源
    最近更新 更多