2012-04-10 1 views
1

これは私がやろうとしていることです。私はこれを共通の問題だと思っていましたが、どういうわけか関連トピックを見つけることができませんでした...Rails 3:ActiveRecordモデルのDBアダプタからの例外の処理

私はスコープの一意性制約を持つモデルを持っています。私はそうのように、移行のテーブルに一意のインデックスを定義することによって、これを行うことにしました:

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

    def down 
    ... 
    end 
end 

このインデックスの一意性に違反するレコードを保存しようとしたときのActiveRecordが例外をスローします。今私はそれを検証エラーとして表示させたいと思います。私は、ActiveRecord :: RecordNotUniqueをLossRatioモデルで捕捉し、意味のあるメッセージでエラーハッシュを取り込むことが最善の方法だと考えました。私はそれのようにした:

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を使用していると考えることができますが、私はしたいが、これを行わない 1つの選択肢は、私は、アプリケーションロジック
  • にそれを透明にします
  • ほとんどの場合、関連付けられたコントローラはありません(これらのオブジェクトは、別のモデルを介した関連付けとしてのみ作成されます)。

任意のインスタンスメソッドからスローされた例外からこのモデルレスキューを行う方法はありますか?私はクラスレベルのレスキュー句を使用しようとしましたが、何もキャッチしません。


もう1つの疑問は、ends_atにARスコープ検証を使用する必要があるかどうかです。 RecordNotUniqueが処理されても、オブジェクトは引き続き有効であるとみなされ、保存に失敗した後にタイムスタンプが設定されます。それは望ましくない副作用を引き起こすことができますか?あなたはまた、(gmaletteのような提案)モデルに一意性を確認する必要があり

validates_uniqueness_of :ends_at, :scope => :tool_id

+0

以下の私のコメントの他に、 'save'は例外を発生させないでください。 trueまたはfalseを返します。 –

+0

この場合、特に、ActiveRecordを迂回するDB制約によって検証が行われる場合、RecordNotUniqueは非bangメソッドによっても生成されます。 – HargrimmTheBleak

答えて

1

。そうすれば、データベースにヒットする前にほとんどのエラーを取得できます。あなたに1つの追加のSELECTが必要ですが、実際の検証があることを確認します。

これを実行すると、2つの独立したプロセスがほぼ同時に競合するデータを挿入しようとすると、データベースインデックスは競合条件のみを解決する必要があります。私は通常、ユーザーに再度試してみるというエラーメッセージを出して、これらのエラーを処理します。

データベースエラーを選択的に処理することは、実際にはむしろエラーが発生する可能性が高いため、実際には好ましくありません。代わりに、可能な限り多くの検証をルビー層で処理し、データベース層をセーフティネットとしてのみ使用してください。

+0

ツールが作成されているときは、検証が実行されるまでにこのインスタンスはdbに保存されないため、IDを持たず、検証するスコープもないため、これは機能しません。したがって、これは更新アクションでのみ機能します。私は実際にはここではさまざまな問題に遭遇しています:(私が見つけたらすぐに完全な解決策を投稿します... – HargrimmTheBleak

2

を試すことができ、検証問題を解決するために

+0

これは私の2番目の質問によく答えますが、もう1つは出ます。 DB-adapter-exceptionの部分を検証するにはどうすればよいでしょうか?validates_uniqueness_of文は、保存時に例外がなくなるため、人為的に何らかの原因で競合状態が発生するのでしょうか?どのような例があるのですか? – HargrimmTheBleak

+0

'model.save(false)'を使用してすべてのバリデーションを無効にすることができます。これらのエラーは非常にまれであり、同時に多数のソースを持つことができます。解決策は、常に再試行するか、何かを変更して再試行することです。より良いエラーを作成することは、(もしあれば)処理することですメッセージ。しかし、正直言って、私は通常これらのエラーを改善するよりも大きなユーザビリティの問題を抱えています:) –

+0

どういうわけか私は忘れてしまった:validate => falseオプション...ありがとう! – HargrimmTheBleak