考虑以下设置:
class Task
has_many :users, through: :task_users
end
class User
has_many :tasks, through: :tasks_users
end
class TaskUser
# Joins table
has_many :comments, dependent: :destroy
end
class Comment
belongs_to :task_user
end
现在,如果我执行标准#destroy
命令,例如:
tu = TaskUser.first
tu.destroy
然后,与任务用户相关的所有评论也将被销毁。
但是,假设您想通过#collection_singular_ids=
更新用户的任务,如下所示:
u = User.first
puts u.task_ids # => [1, 2, 3]
u.task_ids = [1, 2]
执行此操作(甚至不显式调用#save
!)将触发SQL
,如:
(0.3ms) BEGIN
SQL (0.4ms) DELETE FROM `task_users` WHERE `task_users`.`task_id` = 3 AND `task_users`.`user_id` = 1
(2.0ms) COMMIT
...因此,关联的Comment
成为孤儿。
如果您使用#attributes=
:
u.attributes = { task_ids: [1, 2] }
是否有一种干净的方法可以确保相关的Comment
将永远被销毁(即永远不会成为孤儿)?
答案 0 :(得分:0)
感谢@engineersmnky指出我正确的方向。
这可能不是最漂亮的解决方案,但可行的选择是define a callback on the association,例如:
class User
has_many :tasks,
through: :tasks_users,
before_remove: ->(user, task) do
task_user = TaskUser.find_by!(user: user, task: task)
task_user.comments.each(&:destroy!)
end
# Or alternatively, this can be defined as a method:
has_many :tasks,
through: :tasks_users,
before_remove: :destroy_task_user_comments
private
def destroy_task_user_comments(task)
task_user = TaskUser.find_by!(user: self, task: task)
task_user.comments.each(&:destroy!)
end
end
请注意,我在块中使用了bang(!
)方法,因此如果引发异常,整个事务将回滚 - 根据文档:
如果任何
before_remove
回调抛出异常,则不会从集合中删除该对象。