如何在Mongoid中迁移User的deleted_devices至另一User且避免销毁联动?
解决Rails中用户关联记录迁移后不被连带销毁的问题
问题场景
User类的关联定义如下:
has_many :devices, class_name: "Device", validate: false, autosave: true, dependent: :destroy has_many :deleted_devices, class_name: "DeletedDevice", validate: false, autosave: true, dependent: :destroy
调用user1.destroy()时,其关联的devices和deleted_devices会被连带销毁。尝试将user1的deleted_devices合并到user2的关联中时,使用merge!、merge、+=均未达到预期——销毁user1后,user2中合并的记录也被一并移除。
问题原因
你之前的操作只是在内存中把user1的deleted_devices添加到了user2的关联集合里,但数据库中这些记录的user_id外键仍然指向user1。由于User和DeletedDevice的关联设置了dependent: :destroy,销毁user1时,Rails会自动删除所有user_id为user1.id的DeletedDevice记录,包括你以为已经合并到user2的那些。
解决方案
核心是修改这些DeletedDevice记录的归属,把它们的外键指向user2,具体有两种实现方式:
方式一:批量更新(高效,无回调)
直接操作数据库批量修改外键,适合不需要触发模型回调的场景:
# 将user1的所有deleted_devices归属到user2 DeletedDevice.where(user_id: user1.id).update_all(user_id: user2.id) # 重新加载user2的deleted_devices关联,确保内存数据同步 user2.reload_deleted_devices # 此时销毁user1,不会影响已经归属到user2的记录 user1.destroy
方式二:逐个更新(触发回调)
如果需要触发DeletedDevice模型的回调(比如before_save、after_update),可以逐个遍历更新:
user1.deleted_devices.each do |deleted_device| deleted_device.update!(user_id: user2.id) end user2.reload_deleted_devices user1.destroy
可选:事务包裹确保原子性
如果需要保证迁移和销毁操作的原子性(要么全部成功,要么全部回滚),可以用事务包裹:
ActiveRecord::Base.transaction do DeletedDevice.where(user_id: user1.id).update_all(user_id: user2.id) user1.destroy user2.reload_deleted_devices end
为什么之前的方法无效
merge!/merge:仅在内存中合并集合,不会修改数据库的外键归属,记录仍属于user1+=:将内存中的记录添加到user2的关联集合,但未修改数据库中的user_id,销毁user1时仍会被连带删除
内容的提问来源于stack exchange,提问作者Hermione
相关产品推荐
相关产品推荐

