2011-01-20 16 views
0

私はさまざまな状態で多くのレースを持つレーステーブルを持っています。しかし、私は1つのレースだけが= trueのようにマークされていることを保証する必要があります。ここでは、私がレースモデルバリデーションで使用してきたものを示します。ActiveRecord:特定の属性値を持つレコードは1つだけですか?

# current: boolean 
validate :only_one_current 

private 
def only_one_current 
    if self.current && (Race.current_race.id != self.id) 
    errors.add(:base, "Races can have only one current race") 
    end 
end 

これはほとんどの場合動作すると思われますが、ときどきそれが起こりませんし、理由がわかりません。それが機能しない場合は、現在の別のレコードが削除された直後に、current = tという新しいレコードの保存を禁止します。私はそれがARの永続性と関係していると思います。

これを行うには、より良い方法が必要ですか?

答えて

5

あなたの問題は、実際にはActiveRecordを越えて延びています。 before_saveメソッドをどのように実装しても、常に競合状態が発生する可能性があり(意図しない)、2つのレコードがデータベースにcurrent = trueを持つことが可能になります。詳細については、Concurrancy and Integrity section for validates_uniqueness_ofを参照してください。

コアの問題は、レコードがcurrent = trueで、操作がcurrent = trueに設定されているかどうかをチェックするロジックがアトミックではないことです。この問題は、並行システムで頻繁に発生します。

これを解決するには、データベースにunique key indexが必要です。現在のフラグを優先フィールドに変更することをお勧めします。優先度は一意のキーインデックスを持つ整数です。データベースは、同じ優先度の値を持つ2つのレコードが同時に存在しないことを保証します。 「現在の」レースは、常に最高のプライオリティ値を持つレースです。

競合状態は、実際にはまだ存在します。現在、競合状態は検出されています。レースを現在のレースに設定すると(テーブルに最大のプライオリティ値を問い合わせることによって)、保存しようとしているプラ​​イオリティ値と同じプライオリティ値を現在保持しているレコードがあれば、例外が生成されます。重複したキーの例外をキャッチして、やり直してください。

0

あなたはbefore_saveとして、ないバリデータとしてこれを呼び出す必要があります:

before_save :only_one_current 
+0

私はそれを試みたと思う(しばらくしている)。まだ問題があった、正確に覚えていない。コードは大丈夫ですか、それとも良いものがありますか? – Karl

+0

フィルタが最適なオプションです... – sethvargo

+0

フィルタ...リンクや提案はありますか? – Karl