人力検索はてな
モバイル版を表示しています。PC版はこちら
i-mobile

Railsのバリデーションが上手くいかない

nilを許容で、値がある場合にformatのバリデーションをかけたいのですが、formatにそってない値の場合、saveの際に全てnilに変換されて、通ってしまいます。
saveを通さずにバリデーションで警告したいのですが、
いい方法があればご教示下さい。

下記Gistに貼ったような事を試しましたが、どちらもformatに合わない値はnilに勝手に変換されてsaveされてしまいます。
https://gist.github.com/chucker34/f1c9f8d3d3dda7d6ba02
https://gist.github.com/chucker34/b783c95ebab0fecdbbc7

環境は下記です。
ruby '2.1.2'
rails'4.1.4'

宜しくお願いします。


●質問者: Takapin
●カテゴリ:ウェブ制作
○ 状態 :終了
└ 回答数 : 1/1件

▽最新の回答へ

1 ● a-kuma3
●100ポイント ベストアンサー

Ruby on Rails の String#to_date の実装の実装がこんな感じです。

# File 'activesupport/lib/active_support/core_ext/string/conversions.rb', line 43

def to_date
 ::Date.parse(self, false) unless blank?
end
Class: String — Documentation for rails (4.0.0)

irb とかで試してみれば分かりますが、Date#parse は、自由度が かなり高いです。
さすがに 'a' とか渡すと例外が出ますが、'190' なんかでは、きちんと(?)parse してくれちゃいます ><
つまり、質問の二番目の実装では、例外が出ないので rescue に制御が渡らない...


試してませんけど、こんな感じでバリデータを実装してみたらどうでしょうか。

class DateValidator < ActiveModel::EachValidator
 
 def validate_each(record, attribute, value)
 unless value.blank? and /\A\d{4}\-\d{2}\-\d{2}\Z/ =~ value
 record.errors[attribute] << (options[:message] || "2015-01-01の形式で入力してください")
 end
 end

end

Takapinさんのコメント
ありがとうございます。 すごくヒントになりそうです。 ただ試してみましたが、 上記のカスタムバリデーションを使っても、 allow_blank: true ありだと正規表現に合わないものは、空欄で通ってしまう。 (Rspecの出力を見るとnilに変換はされてない?エラーは出てません) allow_blank: true なしだとnilが認められなくなっていまいます。

Takapinさんのコメント
いや変換はされてるのか ちょっと(Rspecの出力を見るとnilに変換はされてない?エラーは出てません) の部分は一旦スルーでお願いします

a-kuma3さんのコメント
アホです >ぼく >|ruby| unless value.blank? and /\A\d{4}\-\d{2}\-\d{2}\Z/ =~ value ||< ↑論理演算子を間違ってます orz 正しくは、こうですよね... >|ruby| unless value.blank? or /\A\d{4}\-\d{2}\-\d{2}\Z/ =~ value ||<

Takapinさんのコメント
あれ適当な文字列も通ってしまいますね。なんでだろ

Takapinさんのコメント
nilになって通ってるのかなこれも

Takapinさんのコメント
あ!いいのが見つかりました ただ何やってるのかよく解らないです・・・ http://stackoverflow.com/questions/13036550/rails-invalid-datetime-on-model-results-in-nil

a-kuma3さんのコメント
>> ただ何やってるのかよく解らないです・・・ << ぼくも、よく解らないので、調べました。 >> http://dora.bk.tsukuba.ac.jp/~takeuchi/index.php?%A5%BD%A5%D5%A5%C8%A5%A6%A5%A7%A5%A2%2Frails%2Fvalidates%A4%C7before_type_cast <hr>validates で指定した検証は通常、すでに DateTime 型に変換され、record.birthday へ代入された値に対して呼び出されます。 ですので、varidates で record.birthday に対して DateTime 型のメソッドを呼び出して検証するなどが可能になるわけです。 << いや、引用が邪魔かも。全部、読んだ方が良いです。 先のコメントにあった stackoverflow のやつは validate_each 側でどうにかしよう、という感じ。 こっち(↓)は、validate_aech を呼ぶ前に、生の値にしちゃえ、というパターン(これは、これで困るでしょ)。 http://ilyasterin.com/blog/2010/12/rails-custom-validation-before-activerecord-typecasting.html

Takapinさんのコメント
ありがとうございます! overstackflowの下記の件ですが、 http://stackoverflow.com/questions/13036550/rails-invalid-datetime-on-model-results-in-nil ``` raw_value = record.send(before_type_cast) if record.respond_to?(before_type_cast.to_sym) raw_value ||= value ``` ここって何を確認してるのか、ご教示頂けませんか?

Takapinさんのコメント
すいません。というより raw_value = record.send(before_type_cast) if record.respond_to?(before_type_cast.to_sym) raw_value ||= value の処理で、何故nilを許容できているのかつかめてないです・・・

a-kuma3さんのコメント
>> の処理で、何故nilを許容できているのかつかめてないです・・・ << ここ、nil を許容している、というところではなく、日付として不当な文字列を渡しているのに nil で保存されちゃう、というところに対応する部分です(だと、思います)。 record が、属性名_before_type_cast というメソッドの呼び出しが可能であれば、それを呼び出して Validator に渡される前に変換される前の生の値を取り出す。 呼び出せてないと raw_value は nil のままだから、value を使う。 その生の値に対して、blank? とか to_datetime で例外が出ないよね、というバリデーションをやってる。 ということだと思います。
関連質問

●質問をもっと探す●



0.人力検索はてなトップ
8.このページを友達に紹介
9.このページの先頭へ
対応機種一覧
お問い合わせ
ヘルプ/お知らせ
ログイン
無料ユーザー登録
はてなトップ