2

我有一些这样的 STI 设置:

class Document < ActiveRecord::Base
  attr_accessible :name, description

  # Basic stuff omitted
end

class OriginalDocument < Document
  has_many :linked_documents, foreign_key: :original_document_id, dependent: :destroy
end

class LinkedDocument < Document
  belongs_to :original_document

  # Delegation, because it has the same attributes, except the name
  delegate :description, to: :original_document
end

现在我想复制并将其LinkedDocument存储为OriginalDocument具有自己的名称的 ,并保留重复的属性值。但是,我的方法失败了,因为在某个地方,副本仍然希望在 after_* 回调中访问其委托方法。

class LinkedDocument < Document
  def unlink_from_parent
    original = self.original_document

    copy = self.becomes OriginalDocument
    copy.original_document_id = nil
    copy.description = original.description
    copy.save 
  end
end

这会抛出一个RuntimeError: LinkedDocument#description delegated to original_document.description, but original_document is nil.

由于保存查询涉及类型,因此执行附加copy.type = 'OriginalDocument'操作是行不通的;UPDATE documents SET [...] WHERE documents.type IN('OriginalDocument') [...]. 这失败了,因为在交易时,对象仍然是 type LinkedDocument

什么是复制对象并让它成为另一个对象的干净方法?我想调用和我想复制的每个属性,但在这样做之前,我想在这里问一下 update_columntype

4

1 回答 1

1

我将在这里添加我的解决方案,以防没有人有更好的解决方案。希望它会帮助某人。

为了让对象成为另一个对象而不会因为 where 子句检查错误的类型而产生错误的查询,我在调用之前手动更新了类型列而不调用任何回调become

# This is for rails3, where +update_column+ does not trigger 
# validations or callbacks. For rails4, use 
#
#   self.update_columns {type: 'OriginalDocument'}
#
self.update_column :type, 'OriginalDocument' 
document = self.becomes OriginalDocument

现在对于分配,有两个问题:首先,属性设置器可能会因为委托而触发异常。其次,我想批量分配的属性没有attr_accessible故意列在 eg 中,因为它们是内部属性。所以我使用了一个带有丑陋 update_column 语句的循环,产生了太多的查询(因为 rails3 没有update_columns)。

original.attributes.except('id', 'name', 'original_document_id').each do |k,v|
  document.update_column k.to_sym, v
end
于 2013-04-17T08:27:44.890 回答