Rails 3:在ActiveRecord模型中处理来自DB适配器的异常

Rails 3:在ActiveRecord模型中处理来自DB适配器的异常,activerecord,ruby-on-rails-3.1,Activerecord,Ruby On Rails 3.1,这就是我想要做的;我认为这是一个常见的问题,但不知为什么我找不到任何相关的话题 我有一个具有作用域唯一性约束的模型。我决定在迁移过程中在表上定义一个唯一的索引,如下所示: class CreateLossRatios < ActiveRecord::Migration def up ... add_index :loss_ratios, [ :tool_id, :ends_at ], :unique => true end def down ..

这就是我想要做的;我认为这是一个常见的问题,但不知为什么我找不到任何相关的话题

我有一个具有作用域唯一性约束的模型。我决定在迁移过程中在表上定义一个唯一的索引,如下所示:

class CreateLossRatios < ActiveRecord::Migration
  def up
    ...
    add_index :loss_ratios, [ :tool_id, :ends_at ], :unique => true
  end

  def down
    ...
  end
end
这使得ActiveRecord在尝试保存违反索引唯一性的记录时引发异常。现在我想让它显示为验证错误。我认为最好的方法是捕获LossRatio模型中的ActiveRecord::RecordNotUnique权限,并用有意义的消息填充错误哈希。我是这样做的:

class LossRatio < ActiveRecord::Base
  belongs_to :tool

  validates :rate, :ends_at, :tool, :presence => true
  validates_numericality_of :rate
  validates_inclusion_of :rate, :in => (0..1)

  %w{ create save }.each do |name|
    %W{ #{name} #{name}! }.each do |method|
      define_method(method) do |*args|
        begin
          super(*args)
        rescue ActiveRecord::RecordNotUnique => ex
          self.errors.add(:ends_at, I18n.t('activerecord.errors.models.loss_ratio.attributes.ends_at.not_unique'))
        end
      end
    end
  end

end
这是可行的,但似乎有点麻烦。我知道我在这里做假设,即如果我添加另一个DB级别的唯一性约束等,会发生什么,但我看不到解决这个问题的方法。在处理此类场景时,是否有更优雅的解决方案/最佳实践? 我能想到的另一种选择是使用rescue_from,但我不想这样做,因为

我认为这个逻辑不属于控制器,我想让它对应用程序逻辑透明 很可能没有关联的控制器,这些对象将通过另一个模型单独作为关联创建,这在我看来更加错误。 有没有办法使这个模型从任何实例方法抛出的异常中拯救出来?我尝试过使用类级别的rescue子句,但它什么都没有


另一个问题是,我是否仍应使用AR范围内的验证来验证。即使使用RecordNotUnique处理,该对象仍会认为自己是有效的,并且在保存尝试失败后设置其时间戳。它会导致任何不必要的副作用吗?

要解决验证问题,您可以尝试


验证唯一性:结束于:scope=>:tool\u id

您还应该像gmalette建议的那样验证模型中的唯一性。这样,您可以在大多数错误到达数据库之前获取它们。虽然它需要您额外选择一次,但它确保您有实际的验证

在这种情况下,只有当两个独立进程几乎同时尝试插入冲突数据时,数据库索引才应该解决竞争条件。我通常通过发出错误消息,要求用户重试来处理这些错误


有选择地处理数据库错误并不是一个好主意,因为这本身通常很容易出错。相反,尝试在ruby层中处理尽可能多的验证,并仅将数据库层用作安全网。

好的,这很好地回答了我的第二个问题,大部分问题都是这样,但又引出了另一个问题。在验证就绪的情况下,如何测试DB适配器异常部分?validates_university_of语句将在保存时使异常消失,那么,我是否应该以某种方式人为地导致竞争条件?我该怎么做?您的上一句话是否意味着像我试图做的那样处理DB异常不是一个好的实践?您可以使用model.savefalse禁用所有验证。至于为用户处理它,我认为这些错误是非常罕见的,并且可以同时有许多来源。解决办法总是要么再试一次,要么改变一些东西再试一次。这由用户决定。所以,若要创建更好的错误消息,那个么我唯一会尝试处理的事情就是创建更好的错误消息。但老实说,比起改进这些错误,我通常有更大的可用性问题:对,不知何故我忘记了:validate=>false选项。。。谢谢除了我下面的评论之外,您不应该认为save不会引发任何异常。它只返回true或false。特别是在这种情况下,当DB约束绕过ActiveRecord进行验证时,RecordNotUnique也会由非bang方法引发。这在创建工具时不起作用,因为在执行验证时,此实例不会保存到DB中,所以它没有id,也没有验证的范围。因此,这只适用于更新操作。事实上,我在这里遇到了很多不同的问题:我会尽快发布一个完整的解决方案。。。