You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用deep_cloneable gem正确克隆ActiveRecord对象A及其关联B、C、D?

解决方案

完全可以用deep_cloneable实现你要的完整克隆效果,关键得处理好D对象同时关联新B和新C的逻辑,之前的写法问题出在没同步替换D的双归属关联。

推荐写法(利用memoize映射)

deep_cloneable自带memoize功能,能自动记录原对象和克隆对象的对应关系,刚好可以用来处理D的双关联替换:

original_a = A.first
new_a = original_a.deep_clone(
  include: [:bs, :cs, :ds],
  memoize: true
) do |original, clone|
  # 给克隆的B、C记录原id(可选,方便后续追溯,不需要可以删掉)
  clone.original_id = original.id if [B, C].include?(original.class)
  # 处理D对象,替换成克隆后的B和C
  if original.is_a?(D)
    clone.b = memo[original.b]
    clone.c = memo[original.c]
  end
end
new_a.save

为啥之前的写法不行?

  • 第一种写法include: [ {bs: :ds}, :cs ]:克隆Ds的时候只处理了B的关联替换,C还是用原来的旧对象,因为Ds是跟着B走的,没同步替换C的关联。
  • 第二种写法include: [ {bs: :ds}, { cs: :ds} ]:等于从B和C两个路径分别克隆Ds,结果生成了两组重复的D,而且关联的是交叉的旧对象(新B配旧C、新C配旧B)。

另一种手动处理的写法

如果不想用memoize,也可以手动匹配克隆后的B和C来关联D:

original_a = A.first
new_a = original_a.deep_clone(include: [:bs, :cs])

# 给每个克隆的B匹配对应的D,并关联到新的C
new_a.bs.each do |cloned_b|
  original_b = B.find(cloned_b.original_id) # 这里需要克隆B时保留原id
  original_b.ds.each do |original_d|
    # 找到对应克隆后的C(假设克隆C时也保留了原id)
    cloned_c = new_a.cs.find { |c| c.original_id == original_d.c_id }
    # 克隆D并关联新B和新C
    cloned_b.ds << original_d.deep_clone.tap do |new_d|
      new_d.b_id = cloned_b.id
      new_d.c_id = cloned_c.id
    end
  end
end

new_a.save

内容的提问来源于stack exchange,提问作者Dan SimBed

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.03 01:55:56