我正在尝试将一条记录“合并”到另一条记录及其所有关系子项中。
例如:
我有 vendor1
和 vendor2
,它们都有许多包含其他 has_many 的关系。例如,一个供应商有很多 purchase_orders,一个采购订单有很多 ordered_items,一个 ordered_item 有很多 received_items。
如果我将 vendor2
的名称更改为与 vendor1
的名称相同,那么我想销毁 vendor2
但移动所有它的 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 through: :ect...
对此有什么直接的解决方案吗?
最佳答案
您需要在您的应用中定义一个合并逻辑。这可能是一个 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):您可以手动检索要合并的数据(如我的示例所示),但这意味着如果有一天您添加另一个与 Vendor 的关系
模型,假设 Vendor
has_many :customers
但忘记将其添加到 VendorMerger,那么它将“静默失败”,因为 VendorMerger 不知道新的关系>:客户
。要解决此问题,您可以动态获取所有引用了 Vendor
的模型(其中列为 vendor_id
或 class_name
选项等于 'Vendor'
或关系是多态的并且 XX_type
列包含一个 'Vendor'
值)并将所有外来者从旧 ID 转换为新 ID .
关于mysql - 将所有记录关系移动到另一条记录,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45963983/