我正在努力"合并"与其所有关系孩子的一条记录到另一条记录中。
例如:
我有vendor1
和vendor2
,它们都有很多包含其他has_many的关系。例如,供应商有许多purchase_orders,而采购订单有许多ordered_items,ordered_item有很多received_items。
如果我将vendor2
的名称更改为与vendor1
的名称相同,那么我想要销毁vendor2
但移动所有内容&# 39; s has_many到vendor1
。
这就是我一直在尝试做的事情:
def vendor_merge(main_vendor, merge_vendor)
relationships = [
merge_vendor.returns, merge_vendor.receiving_and_bills,
merge_vendor.bills, merge_vendor.purchase_orders, merge_vendor.taxes,
Check.where(payee_id: merge_vendor.id, payee_type: "Vendor"),
JournalEntryAccount.where(payee_id: merge_vendor.id)
]
relationships.each do |relationship|
class_name = relationship.class.name
relationship.each do |r|
if class_name === "Check"
r.update(payee_id: main_vendor.id)
else
r.update(vendor_id: main_vendor.id)
end
r.save
end
relationship.delete_all
end
merge_vendor.destroy
end
这样做会给我带来约束错误,因为has_many的has_many和has_many通过:: ect ...
对此有任何直接的解决方案吗?
答案 0 :(得分:3)
您需要在应用中定义合并逻辑。这可能是一个PORO(普通的旧ruby对象),如VendorMerger
,它包含所有逻辑,以便将Vendor
记录合并到另一个(这也可以在Vendor
模型中但它会污染你的模型。)
以下是该PORO的一个例子:
# lib/vendor_merger.rb
class VendorMerger
def initialize(vendor_from, vendor_to)
@vendor_from = vendor_from
@vendor_to = vendor_to
end
def perform!
validate_before_merge!
ActiveRecord::Base.connection.transaction do # will rollback if an error is raised in this block
migrate_related_records!
destroy_after_merge!
end
end
private
def validate_before_merge!
raise ArgumentError, 'Trying to merge the same record' if @vendor_from == @vendor_to
raise ArgumentError, 'A vendor is not persisted' if @vendor_from.new_record? || @vendor_to.new_record?
# ...
end
def migrate_related_records!
# see my thought (1) below
@vendor_from.purchases.each do |purchase|
purchase.vendor = @vendor_to
# ...
purchase.save!
end
end
def destroy_after_merge!
@vendor_from.reload.destroy!
end
用法:
VendorMerger.new(Vendor.first, Vendor.last).perform!
此PORO允许您将与合并相关的所有逻辑包含在一个文件中。它尊重SRP(单一责任原则),使测试变得非常简单,并且维护(例如:包括记录器,自定义错误对象等)。
思想(1):您可以手动检索要合并的数据(如我的示例所示),但这意味着如果有一天您将另一个关系添加到{{1} }模型,让我们说Vendor
Vendor
但是忘了将它添加到VendorMerger,然后它会“无声地失败”,因为VendorMerger不知道新关系has_many :customers
。要解决此问题,您可以动态获取所有引用:customers
的模型(其中列为Vendor
或vendor_id
选项等于class_name
或者关系是多态的'Vendor'
列包含XX_type
值,并将所有外国人从旧ID转换为新ID。