rails における enum の validation
はいどうもこんにちは!みんなのウェディングのエンジニア@jaruです。
この記事は、くふうカンパニーアドベントカレンダーの24日目になります。
Rails における enum とは
突然ですが、皆さんは rails における enum をご存知ですか?
そして以下は、Blog の状態を表現するために設定された enum の例です。
class Blog < ApplicationRecord enum state: { draft: 0, unpublish: 1, publsihed: 2 } end
このように enum を設定することで、Blog が下書き
なのか、公開中
なのか、Blog の状態
を表現することができます。
Rails における enum の validation
そして日頃から enum を使っている方は、enum にもちゃんと validation をかけたいなと思ったことはありませんか?ありますよね!
そしておそらく、普段から rails を書いている人はこんな風に書くのではないのでしょうか
validates :state, inclusion: { in: %w(draft unpublish publsihed) }
結論から言うと、この validation は全く機能しません。
そんなバカな!と僕も思いました。inclusion なんて enum の validation をかけるのにぴったりじゃないか!と。でもこれは本当に全く機能しないんですよね。以下、console で色々試してみたいと思います。
実験
以下のように、Blog モデルに enum と validation を設定しています。
class Blog < ApplicationRecord validates :state, inclusion: { in: %w(draft unpublish publsihed) } enum state: { draft: 0, unpublish: 1, publsihed: 2 } end
ここに state が draft
になっている一つの blog があります。
ありとあらゆる方法を使って、こいつにinvalid_params
という文字列を保存させてみたいと思います。
①セットしてから save してみる
ArgumentError
が返ります。
②update してみる
ArgumentError
③asssign_attribute してから update してみる
アーギュメントエラァ…
④update_columns してみる
お? true ですか?! update できちゃったんですか?!? 更新されてませんでした(意図せずバグっぽいのを発掘してしまった…)
ほとんど全てで ArgumentError
が返る
そうなんです。enum で設定している"draft"
, "unpublish"
, "published"
以外の文字列(シンボルでも同様)を渡そうとすると、validation に到達する前に(おそらく確実に)ArgumentError
が発生します(一部例外がありましたが…)。なぜこのような設計になっているのかは以下のissueが参考になります。
じゃあどうする?
モデルでは validation できないとは言いつつも、本当にごく稀に何者かが enum で設定している以外の parameter を投げつけてきて、エラーが発生してしまって、alert が発生してしまって、つらいな…みたいなこともありますよね。 というわけで、enum が定義されているときの validation は、個人的には以下のように例外処理を書いてしまいます。
def update begin @blog.update blog_params # update 成功時の処理 rescue ArgumentError # 例外発生時の処理 end end
はい、これで変な parameter を投げつけられても alert が発生せずハッピーですね!
まとめ
- enum はあくまでも状態のセットを定義するための機能
validates :state, inclusion: { in: %w(draft unpublish publsihed) }
は機能しない- controller で、例外処理、もしくは filter でチェックする
最後まで読んでいただいてありがとうございました!
明日のラストは、kazumalabさんの年内最後に振り返ります
です!