Ruby on rails Rails在销毁所有时抛出RecordNotDestroyed异常
我有一个批处理工作程序,它通过清理一个表然后重新填充来遍历和更新数据库。应该防止数据库中的特定行被销毁,因此我在销毁之前有一个Ruby on rails Rails在销毁所有时抛出RecordNotDestroyed异常,ruby-on-rails,ruby,activerecord,Ruby On Rails,Ruby,Activerecord,我有一个批处理工作程序,它通过清理一个表然后重新填充来遍历和更新数据库。应该防止数据库中的特定行被销毁,因此我在销毁之前有一个:dont\u destroy\u record回调,以确保我不删除特定记录 我遇到的问题是,active record在调用collection.destroy\u all,ActiveRecord:RecordNotDistromed时引发异常。每当:dont\u destroy\u record回调为这些特定记录返回false时,就会引发此错误 这个问题的两个解决方
:dont\u destroy\u record
回调,以确保我不删除特定记录
我遇到的问题是,active record在调用collection.destroy\u all
,ActiveRecord:RecordNotDistromed
时引发异常。每当:dont\u destroy\u record
回调为这些特定记录返回false时,就会引发此错误
这个问题的两个解决方案是包装集合。在try catch中销毁\u all
,或者我可以循环调用每个对象上的destroy
集合。这个问题还有其他可能的或更好的解决方案吗?一个限制是,我不能太多地更改这个批处理工作程序,因为它在许多其他项目中使用,并且需要是超级通用的
我觉得奇怪的是,
destroy\u all
会抛出该异常,因为它正在调用destroy
。我在文档中也没有看到太多关于抛出异常的内容。对这个问题有一些很好的评论,我想我会给出一个完整的答案,其中包括一些评论的要点,以及每种方法的代码示例和上/下分析
拯救
这样,您就可以将destroy_all
分别展开到destroy
每条记录。这将允许您检测在每条记录上何时引发异常,只需忽略异常并继续:
collection.each do |record|
begin
record.destroy
rescue e => ActiveRecord:RecordNotDestroyed
puts "Record #{record.id} not destroyed"
end
end
缺点:
- 有些人可能会发现这种未启蒙的红宝石,或者只是不好的做法
- 这当然有效
- 它通过了测试
dont\u destroy\u record
,则您可以预先测试集合并仅销毁通过集合的记录
safe_to_destroy = collection.select {|record| !record.dont_destroy_record }
safe_to_destroy.destroy_all
缺点:
- 您的代码必须能够访问
功能不销毁记录
不应有副作用不要销毁记录
- 这将使
中的记录调用safe\u to\u destroy
的次数增加一倍,因此可能会影响性能dont\u destroy\u record
- 如果
的结果在对给定记录的第一次和第二次调用之间可能发生变化(用户活动、后台进程等),则可能存在竞争条件dont\u destroy\u record
- 根据上述条件,只能对可安全销毁的记录调用
destroy\u all
集合的查询。此查询将包含在验证函数中检查的相同条件
让我们假设这是无灵魂公司的一项任务,他们准备有选择地分离(解雇)至少工作30天的高薪员工,作为其每月清理流程的一部分。最初的查询可能如下所示:
collection = Person.joins(:employees).where(salary_level: :highly_compensated).where("person.hire_date < ?", DateTime.now - 30)
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")
我们可以将其滚动到单个查询中,想象如下:
collection = Person.joins(:employees).where(salary_level: :highly_compensated).where("person.hire_date < ?", DateTime.now - 30)
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")
collection=Person.joins(:employees).where(薪资水平:高薪酬).where(“Person.hire\u date<?”,DateTime.now-60).not(角色::boss).group(员工id).have(“COUNT(employees.id)=0”)
在我们想象的模型中使用这种方法,我们可以得到一个经过过滤的集合,然后我们可以安全地使用destroy_all
。然后每个人都很高兴,除了那些非自愿参与无灵魂公司每月职业搬迁计划的员工
缺点:
- 需要重新思考和测试查询
- 需要访问权限才能更改查询
- 需要保持查询逻辑与验证逻辑同步
正面:
- 可以显著减少数据库查询开销
- 调用destroy_all不需要显式的错误处理/避免机制
- 你不是为没有灵魂的公司工作
在销毁
回调之前,您不需要在中决定是否销毁某个内容,而是可以将初始集合筛选为您确实要销毁的集合,并且只销毁这些集合吗?不,因为筛选集合需要我更改批处理工作程序,而这将不起作用。您需要进行循环然后收集,因为这就是销毁所有正在做的。只需展开它;你甚至可以在循环中手动调用回调,比如,records.each{{{record}next if record.dont_destroy_record;record.destroy}或者你可以在循环中使用rescue(有些人认为rescue是缓慢的、懒惰的或糟糕的编程,这是他们的观点,伙计)如果你使用destroy
而不是destroy!
它不应该引起错误,因此你甚至不需要捕捉它-只需在该循环中留下一条注释,以便将来你知道它可能不会真的破坏它:)好吧,法院3日。大多数人都不赞成为“预期”建立一个援救条款“错误。你的记录数据集有多大?很好的总结!小错误,我会用替换不销毁记录(record).nil?
!!record.Don_destroy_record
-因为方法在记录上,所以不销毁记录,false.nil?==false:)“有些人可能会发现这是一个未启蒙的Ruby,或者只是一个坏习惯”->因为您正在拯救一个您希望看到抛出的异常;然而,根据其经典定义,救援应该是意想不到的