普通にモデルを作っていて、一意性を確保したい属性には、validates_uniqueness_ofでチェックするのがRails流。
スマートにかけていいよね~とか思っていると、ここに落とし穴が。
まず、MySQLをRDBMSに利用している場合のみらしいが、
オプションの :case_sencitive が効かない
大文字小文字を認識してくれないのであるよ。
同じ理由で、ActiveRecord.countもActiveRecord.findも正しくない結果が帰ってくる。
で、ActiveRecord.find/countの時は、:conditionsオプションを指定するときに、
BINARY句を追加する。
こんな感じ
Hoge.find(:conditions=>["BINARY name = ?", "hoge"])
まあ、これはいいとして、validates_uniqueness_ofではどうするか、ということになる。
手っ取り早いが根本的でない解決法は、validates_uniqueness_ofを使わない、という選択肢。
代わりにvalidateメソッドを定義して、
そこでcountを発行して存在するかをチェックする。
このとき、自身がnew_record?かどうかで、自分を除外するコードを足すかどうかを判別する必要がある。
根本的な解決は、ActiveRecord/validate.rbのvalidates_uniqueness_ofをオーバーライドする事になる。
属性の型を認識して、BINARY句を追加するか判別しないといけない。
しかし、更に問題なのはvalidate_uniquness_ofではレコードの一意性が保証されないこと。
いわゆる、トランザクションとかロックの問題なんだけど、これはDBMSに丸投げするのが正解。
マイグレーションを使っているなら最後にユニークインデックスを張ってやる
add_index :tabel_name, :colun_name, :unique=>true
これは、validates_uniqueness_ofを使っている以上はどの場合もそうらしい。
やれやれ。
PR