使用 join table 更新记录会创建重复条目

Updating record with join table creates duplicate entries

我有3个型号如下:

class Document < ActiveRecord::Base
  has_many :documents_tasks, inverse_of: :document
  has_many :tasks, through: :documents_tasks, dependent: :destroy
end

class Task < ActiveRecord::Base
  has_many :documents_tasks, inverse_of: :task
  has_many :documents, through: :documents_tasks, dependent: :destroy
end

class DocumentsTask < ActiveRecord::Base
  belongs_to :task, inverse_of: :documents_tasks
  belongs_to :document, inverse_of: :documents_tasks

  validates_uniqueness_of :document_id, scope: :task_id
end

在上面,当我尝试更新 Task 的记录时,如果我保留验证,它会抛出 DocumentsTask 模型上重复条目的验证错误,或者如果删除,则直接插入重复项验证。

我更新任务记录的代码是:

def update
  @task = @coach.tasks.find(params[:id])
  @task.update(:name => task_params[:name], :description => task_params[:description] )
  @task.documents << Document.find(task_params[:documents])
  if @task.save
    render 'show'
  else
    render status: 500, json: {
        error: true,
        reason: @task.errors.full_messages.to_sentence
    }
  end
end

我知道我可以将 unique index 添加到数据库以自动防止重复条目,但是有什么方法可以防止 controller 在连接 table 值时更新它们'一样吗?

因此,当我尝试更新相关文档时,例如:

它试图将文档 5 和 6 重新添加到数据库中,所以我得到了错误:

Completed 422 Unprocessable Entity in 9176ms

ActiveRecord::RecordInvalid (Validation failed: Document has already been taken)

这是因为我添加了以下验证:

validates_uniqueness_of :document_id, scope: :task_id

在我的 DocumentsTask 模型中,如上所示。问题是如何防止它尝试重新添加现有记录

假设 task_params[:documents] 是一个文档 ID 数组(基于您现在如何将它与 find 一起使用),您应该可以像这样快速修复:

@task.documents << Document.where(id: task_params[:documents]).where.not(task_id: @task.id)

基本上这只是在将它们分配给任务之前过滤掉已经与给定任务关联的文档。

也就是说,我建议采用更强大的方法作为长期解决方案。几个选项(在许多选项中)将任务创建的责任提取到它自己的 class 中(因此您可以更轻松地测试它并使该功能更具可移植性),或者您可以考虑覆盖 setter 任务模型中文档的方法类似于此答案描述的内容: