2009-04-07 16 views
6

アクティブレコードのvalidates_uniqueness_ofvulnerable to race conditionsです。本当に一意性を保証するには、追加のセーフガードが必要です。 ActiveRecordのRDocsから1つの提案は、あなたの移行に含めることによって、例えば、データベース上の一意のインデックスを作成することです:ActiveRecordオブジェクトが一意のデータベースのキー/インデックスに違反しているかどうかを確認するにはどうすればよいですか?

add_index :recipes, :name, :unique => true 

これは、名前が一意であることをデータベース・レベルで保証されます。しかし、このアプローチの欠点は、重複を保存しようとしたときに返される例外がそれほど役に立ちませんということです。エラーが複製されたレコードによって生成されただけでなく、壊れたSQLによって生成されたというこの例外をキャッチするときには、必ずしも確実ではありません。

RDocsが示唆しているように、1つの解決策は例外に付属するメッセージを解析し、「重複」または「一意」のような単語を検出しようとしますが、これはkludgyであり、メッセージはデータベースバックエンド固有です。 SqlLite3の場合、私の理解は、メッセージは完全に汎用的であり、このように解析することはできません。

これはActiveRecordユーザーにとって根本的な問題であることを考えると、これらの例外を処理するための標準的なアプローチがあるかどうかを知ることは良いことです。私は下に私の提案を提供します。コメントしてください。ありがとう!

答えて

7

エラーメッセージを解析することはそれほど悪くないが、クルージングを感じる。私が遭遇した提案(どこを覚えていないか)は、レスキューブロックでデータベースをチェックして実際に重複レコードがあるかどうかを調べることができます。存在する場合は、複製が原因でStatementInvalidが発生する可能性があり、それに従って適切に処理できます。存在しない場合、StatementInvalidは他のものからのものでなければならず、別の方法でそれを処理する必要があります。

だから、基本的な考え方は、上記のようにrecipe.nameに一意のインデックスを仮定:

begin 
    recipe.save! 
rescue ActiveRecord::StatementInvalid 
    if Recipe.count(:conditions => {:name => recipe.name}) > 0 
    # It's a duplicate 
    else 
    # Not a duplicate; something else went wrong 
    end 
end 

を私は次のように、このチェックを自動化しようとした:

class ActiveRecord::Base 
    def violates_unique_index?(opts={}) 
    raise unless connection 
    unique_indexes = connection.indexes(self.class.table_name).select{|i|i.unique} 
    unique_indexes.each do |ui| 
     conditions = {} 
     ui.columns.each do |col| 
     conditions[col] = send(col) 
     end 
     next if conditions.values.any?{|c|c.nil?} and !opts[:unique_includes_nil] 
     return true if self.class.count(:conditions => conditions) > 0 
    end 
    return false 
    end 
end 

だから今、あなたが使用することができるはずStatementInvalidの処理方法を決めるレスキューブロックのgeneric_record.violates_unique_index?

希望がありますように!他のアプローチ?

+0

私はこれを実装し、仕事を終えたようです。これは本質的に同じことをより効率的に行うので、validates_uniquenessを取り出すことを強いられました。関連するアップデートを投稿します。 – Chinasaur

+0

非常に小さなニック・ピック:例外を取得して複製を照会するまでの間に複製が削除されると間違ったエラーが発生します。 – mpartel

2

これは本当に大きな問題ですか?あなたはvalidates_uniqueness_of制約と一緒にユニークなインデックスを使用している場合

、その後、

  • データの整合性が
  • 維持される2つの別々の の要求が非を挿入しようとすると、最悪の場合 のみエラーになります - ユニーク 行同時に

あなたが多くの潜在的な重複挿入を行うアプリを持っていない限り(その場合、私は再デザインを見ますそれは実際にはめったに問題にはならないと私は思っている)。

+0

あなたは例外をめったに取得しません。しかし、私はまだそれを処理することがいいと思った:)。 あなたの答えに由来する1つの質問:私がそれを避けることができるなら、 'validates_uniqueness_of'を使用したいと思いますか?私の答えがうまくいくなら、私は少なくともそれを投げるように誘惑されます: – Chinasaur