救援异常并继续处理的正确方法是什么?我有一个具有文件夹和项目的应用程序,通过名为folders_items的连接表具有habtm关系。该表具有唯一约束,确保没有重复的项目/文件夹组合。如果用户尝试多次将项目添加到同一文件夹,我显然不希望添加其他行;但我也不想停止处理。
当违反唯一约束时,Postgres会自动抛出异常,所以我试着在控制器中忽略它,如下所示:
rescue PG::Error, :with => :do_nothing
def do_nothing
end
这适用于单次插入。控制器执行渲染,状态代码为200.但是,我有另一种方法在循环中执行批量插入。在该方法中,控制器在遇到第一个重复行时退出循环,这不是我想要的。起初,我认为循环必须包含在正在回滚的事务中,但事实并非如此 - 重复插入之前的所有行。我希望它只是忽略约束异常并移动到下一个项目。如何防止PG :: Error异常中断此操作?
答案 0 :(得分:13)
通常,您的异常处理应位于错误的最近点,您可以使用异常做一些合理的事情。在您的情况下,您需要在循环中使用rescue
,例如:
stuff.each do |h|
begin
Model.create(h)
rescue ActiveRecord::RecordNotUnique => e
next if(e.message =~ /unique.*constraint.*INDEX_NAME_GOES_HERE/)
raise
end
end
有几点兴趣:
ActiveRecord::RecordNotUnique
错误,而不是基础PG::Error
。 AFAIK,如果您直接与数据库交谈而不是通过ActiveRecord,您将获得PG::Error
。INDEX_NAME_GOES_HERE
替换为唯一索引的真实姓名。next if(...)
位后跟无争论raise
(即如果它不是您的话,则重新引发异常)期待看到。)答案 1 :(得分:1)
如果您在模型上放置了Rails验证器,那么您可以控制流量而不会抛出异常。
class FolderItems
belongs_to :item
belongs_to :folder
validates_uniqueness_of :item, scope: [:folder], on: :create
end
然后你可以使用
FolderItem.create(folder: folder, item: item)
如果创建了关联,它将返回true,如果存在错误,则返回false。它不会抛出异常。如果未创建关联,则使用FolderItem.create!
会抛出异常。
您看到PG错误的原因是因为Rails本身认为模型在保存时有效,因为模型类在Rails中没有唯一性约束。当然,你在数据库中有一个独特的约束,这会让Rails感到惊讶并导致它在最后一分钟爆炸。
如果表现很关键,那么可能会忽略这个建议。在Rails模型上具有唯一性约束会导致它在每SELECT
之前执行INSERT
,以便在Rails级别执行唯一性验证,从而可能使循环执行的查询数量翻倍。只是像你一样在数据库级别捕获错误可能是一种合理的性能交易。
(编辑)TL; DR:始终在DB中具有唯一约束。同样具有模型约束将允许在DB抛出错误之前进行ActiveRecord / ActiveModel验证。