如何通过关联在 has_many 中使用回调?

Posted

技术标签:

【中文标题】如何通过关联在 has_many 中使用回调?【英文标题】:Howto use callbacks in a has_many through association? 【发布时间】:2012-05-22 19:06:26 【问题描述】:

我有一个通过 has_many 关联到项目模型的任务模型,并且需要在通过关联删除/插入之前操作数据。

由于“Automatic deletion of join models is direct, no destroy callbacks are triggered.”我不能为此使用回调。

在任务中,我需要所有 project_ids 来计算保存任务后项目的值。 如何通过关联禁用删除或更改删除以销毁 has_many? 这个问题的最佳做法是什么?

class Task
  has_many :project_tasks
  has_many :projects, :through => :project_tasks

class ProjectTask
  belongs_to :project
  belongs_to :task

class Project
  has_many :project_tasks
  has_many :tasks, :through => :project_tasks

【问题讨论】:

【参考方案1】:

似乎我必须使用associations callbacksbefore_addafter_addbefore_removeafter_remove

class Task
  has_many :project_tasks
  has_many :projects, :through => :project_tasks, 
                      :before_remove => :my_before_remove, 
                      :after_remove => :my_after_remove
  protected

  def my_before_remove(obj)
    ...
  end

  def my_after_remove(obj)
    ...
  end
end   

【讨论】:

【参考方案2】:

这就是我所做的

在模型中:

class Body < ActiveRecord::Base
  has_many :hands, dependent: destroy
  has_many :fingers, through: :hands, after_remove: :touch_self
end

在我的 Lib 文件夹中:

module ActiveRecord
  class Base
  private
    def touch_self(obj)
      obj.touch && self.touch
    end
  end
end

【讨论】:

【参考方案3】:

更新加入模型关联,Rails 添加和删除集合上的记录。要删除记录,Rails 使用delete 方法,这个方法不会调用任何destroy callback。

您可以强制 Rails 在删除记录时调用 destroy 而不是 delete。 为此,请安装 gem replace_with_destroy 并将选项 replace_with_destroy: true 传递给 has_many 关联。

class Task
  has_many :project_tasks
  has_many :projects, :through => :project_tasks,
            replace_with_destroy: true
  ...
end

class ProjectTask
  belongs_to :project
  belongs_to :task

  # any destroy callback in this model will be executed
  #...

end

class Project
  ...
end

这样,您可以确保 Rails 调用所有 destroy callbacks。如果您使用的是paranoia,这可能非常有用。

【讨论】:

【参考方案4】:

似乎将dependent: :destroy 添加到has_many :through 关系会破坏连接模型(而不是删除)。这是因为CollectionAssociation#delete 内部引用:dependent 选项来确定是否应该删除或销毁传递的记录。

所以在你的情况下,

class Task
  has_many :project_tasks
  has_many :projects, :through => :project_tasks, :dependent => :destroy
end

应该可以。

【讨论】:

以上是关于如何通过关联在 has_many 中使用回调?的主要内容,如果未能解决你的问题,请参考以下文章

如何通过关联为 has_many 定义工厂

如何通过 has_many 关联在 FactoryBot 中设置工厂

如何使用带有 has_many 的 PaperTrail 版本控制:通过在 Rails 4 中实现关联

Rails has_many,如何实现嵌套关联?

如何在has_many中获取模型的属性:通过Rails 5中的关联

如何通过“has_many”关联获取数据?