Rails在destroy_all上抛出RecordNotDestroyed异常

时间:2016-05-16 19:25:29

标签: ruby-on-rails ruby activerecord

我有一个批处理工作程序,通过清理表然后重新填充它来更新数据库。应该防止数据库中的特定行被破坏,因此我有before_destroy :dont_destroy_record回调以确保我不删除特定记录。

我遇到的问题是,当调用collection.destroy_allActiveRecord:RecordNotDestroyed时,活动记录会抛出异常。只要:dont_destroy_record回调为这些特定记录返回false,就会抛出此错误。

问题的一些解决方案是将collection.destroy_all包装在try-catch中,或者我可以遍历每个对象上调用destroy的集合。这个问题还有其他可能的或更好的解决方案吗?一个限制因素是我无法更改此批处理工作程序,因为它在很多其他项目中使用并且需要超级通用。

我觉得destroy_all会抛出异常,因为它正在调用destroy,这似乎很奇怪。我在关于抛出异常的文档中也没有多少看到。

1 个答案:

答案 0 :(得分:1)

这个问题有一些很好的评论,我想我会整理一个完整的答案,其中包括一些评论的要点,以及代码示例和每种方法的上行/下行分析。

救援

这样,您可以单独展开destroy_alldestroy每个记录。这将允许您检测何时在每条记录上抛出异常,并且只是忽略该异常并继续:

collection.each do |record|
  begin
    record.destroy
  rescue e => ActiveRecord:RecordNotDestroyed
    puts "Record #{record.id} not destroyed"
  end
end

缺点:

  • 有些人可能会发现这种没有启发的Ruby,或者只是不好的做法

好处:

  • 肯定有效
  • 它通过了GSD测试

在销毁

之前使用验证功能过滤记录

如果您可以访问验证函数dont_destroy_record,则可以预先测试集合,并仅销毁那些通过集合的记录。

safe_to_destroy = collection.select {|record| !record.dont_destroy_record }
safe_to_destroy.destroy_all

缺点:

  • 您的代码必须能够访问dont_destroy_record函数
  • dont_destroy_record不应该有副作用
  • 这使dont_destroy_record中记录的safe_to_destroy调用次数加倍,因此可能会影响性能
  • 如果dont_destroy_record的结果在给定记录的第一次和第二次调用之间发生变化(用户活动,后台进程等),则可能存在竞争条件

好处:

  • 根据上述标准,您只能对可以安全销毁的记录调用destroy_all

将查询改进为仅返回可销毁记录

这需要访问返回要销毁的collection的查询。此查询将包括在验证函数中检查的相同条件。

让我们假设这是 Soulless Corp 的任务,并且他们准备选择性地分离(解雇)在工作中至少有30天工作的高薪员工。他们的每月清除过程。原始查询可能如下所示:

collection = Person.joins(:employees).where(salary_level: :highly_compensated).where("person.hire_date < ?", DateTime.now - 30)

还假设验证检查确保Person对象不是老板或没有分配人员。也许条件看起来像这样:

return false if role == boss && employees.any?

我们可以将其转换为单个查询,想象它是这样的:

collection = Person.joins(:employees).where(salary_level: :highly_compensated).where("person.hire_date < ?", DateTime.now - 60).not(role: :boss).group(employee_id).having("COUNT(employees.id) = 0")

将这种方法与我们的虚构模型一起使用会为我们提供一个过滤后的集合,然后我们就可以安全地使用destroy_all。除了非自愿参加 Soulless Corp 每月职业搬迁计划的员工外,每个人都很开心。

缺点:

  • 需要重新思考和测试查询
  • 需要访问权才能更改查询
  • 要求使查询逻辑与验证逻辑保持同步

好处:

  • 可能会显着减少数据库查询开销
  • 对destroy_all的调用不需要显式的错误处理/避免机制
  • 您不适用于 Soulless Corp