我有一些像这样的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') [...]
。这失败了,因为在事务处理时,对象仍然是LinkedDocument
类型。
复制对象并让它成为另一个对象的干净方法是什么?我想为update_column
调用type
以及我要复制的每个属性,但在这样做之前,我想问这里。
答案 0 :(得分: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
故意因为它们是内部属性。所以我使用了一个带有丑陋的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