Rails, Attachment_fu - 数据库存储附件的深拷贝

发布于 2024-08-20 12:21:29 字数 2202 浏览 3 评论 0原文

我有一个模型,比方说 Attachments,它使用 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里面我看到了save方法:

      # 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源代码显示没有提到这一点,我在谷歌上也找不到它)。

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

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

 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

这实际上对于克隆对象来说效果很好,但不幸的是,常规非克隆文件上传的所有测试都失败了。 Attachment 对象已创建,但没有数据写入 db_file。理论上是先保存父对象,然后再写入 db_file 的东西,因此 new_record?返回假。

因此,作为一个实验,我决定尝试:

  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 为零。

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

谢谢!!

I have a model, let's say Attachments, that uses attachment_fu to accept file uploads from the user. I want to "deep copy" (or in Ruby-ese, deep clone) an Attachment, thus creating a completely new binary object in the "db_files" table.

I've found that it is not quite a solved problem yet. This blog posting:
http://www.williambharding.com/blog/rails/rails-faster-clonecopy-of-attachment_fu-images/

Shows a method that allegedly works for filesystem based storage. For db-based stores, the "deep copy" fails. A new "Attachment" is created but it uses the pre-existing db_file_id, thus performing a shallow copy.

Inside attachment_fu's db_file_backend.rb I see the save method:

      # 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

So, I am trying to decipher this and I believe "build_db_file" is some Ruby metaprogramming magic shorthand for DbFile.new although I cannot confirm this (grepping the source shows no mention of this, nor can I find it on google).

I'm not quite sure what it is doing, but my theory is that the db_file is being copied from the source obj as part of the "Deep copy" attempt (in the linked code) thus it is simply triggering a save instead of a create.

My initial theory was that the parent (Attachment) object would be set to "new" upon a deep copy attempt, thus I did something like:

 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

This actually works fine for cloned objects but unfortunately all the tests for regular, non cloned file uploads fail. The Attachment object is created but no data is written to db_file. Theory is that the parent object is saved first, then the db_file stuff is written later, thus new_record? returns false.

So, as an experiment I decided to try:

  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

That works partially - the db_file is populated but then I get an error on db_file.save! - saying that db_file is nil.

So, I'm sort of stymied. I can do some further trial and error but at this point I've hit my limited understanding of how this plugin works. I really didn't expect or want to spend this much time on it so I am reluctant to try and explore attachment_fu any further, but I'm afraid I'm going to have to go down the rabbit hole to figure it out. Any ideas or thoughts?

Thanks!!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

那些过往 2024-08-27 12:21:29

这只是解释 build_db_file 调用的部分响应

正如您所怀疑的,build_db_file 调用执行一个方法 通过创建 belongs_to 关联生成。关联是在此处创建的:

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 分配给它的二进制字段data。 temp_data 可能是包含表单数据的字节数组。

我有一个问题(我无法评论你的问题) - 为什么你在使用 db_file.save 创建后不调用 db_file.save!

db_file = DbFile.new :data => temp_data

This is just a partial response explaining the build_db_file call

As you suspected, the build_db_file call executes a method generated by creating a belongs_to association. The association is created here:

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

So the (db_file || build_db_file) statement takes an existing associated DbFile object, or creates a new one if it's nil, and assigns the temp_data to its binary field data. The temp_data is probably the byte array with the data from the form.

And I have one question (I can't comment on your question) - why don't you call db_file.save! after creating it with

db_file = DbFile.new :data => temp_data

?

提赋 2024-08-27 12:21:29

好吧,所以我没有弄清楚如何创建新的 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

Okay, so instead of figuring out how to create a new db_file (which is wasteful in our particular case), I just monkey-patched destroy_file to only delete the db_file if there are no more attachment records pointing to it. This may not be appropriate if you allow someone to "modify" an attachment db_file in situ but since we don't, this works great.

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
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文