バリデーション(検証)
Active Recordを使って、モデルがデータベースに書き込まれる前にモデルの状態をバリデーション(検証: validation)
できます。Active Recordにはモデルチェック用のさまざまなメソッドが用意されており、属性が空でないかどうか、属性が一意かどうか、既にデータベースにないかどうか、特定のフォーマットに沿っているかどうか、多岐にわたったバリデーションが行えます。
バリデーションは、データベースを永続化するうえで極めて重要
です。そのため、save、updateメソッドは、バリデーションに失敗するとfalseを返します。このとき実際のデータベース操作は行われません。上のメソッドにはそれぞれ破壊的なバージョン (save!、update!) があり、こちらは検証に失敗した場合にさらに厳しい対応
、つまりActiveRecord::RecordInvalid例外を発生します。以下はバリデーションの簡単な例です。
class User < ApplicationRecord
validates :name, presence: true
end
irb> user = User.new
irb> user.save
=> false
irb> user.save!
ActiveRecord::RecordInvalid: Validation failed: Name can't be blank
出典
気づき
バリデーションとsave!やupdate!など例外を発生させるメソッドを組み合わせることで例外処理ができるのか。
モデルレベルのバリデーションが最適
Railsチームは、ほとんどの場合モデルレベルのバリデーションが最も適切
であると考えています。
出典
気づき
大体の場合は、モデルでバリデーションを行うのがいいのか。
関連記事 バリデーションレベルに関して ("モデルレベル rails"で調べた)
共通のバリデーションオプション
:message
オプション
既に例示したように、:messageオプションを使うことで、バリデーション失敗時にerrorsコレクションに追加されるカスタムエラーメッセージを指定
できます。このオプションを使わない場合、Active Recordはバリデーションヘルパーごとにデフォルトのエラーメッセージを使います。:messageオプションはStringまたはProcを受け取ります。
Stringの:message値には、%{value}や%{attribute}や%{model}をオプションで含められます
。これらはバリデーション失敗時に動的に置き換えられます。置き換えにはI18n gemが用いられており、プレースホルダーの文字はこのとおりでなければならず、スペース文字を含めることはできません。
Procの:message値には引数が2つ与えられます。バリデーションの対象となるオブジェクトと、:modelと:attributeと:valueのキーバリューペアを含むハッシュです。
class Person < ApplicationRecord
# メッセージを直書きする場合
validates :name, presence: { message: "省略できません" }
# 動的な属性値を含むメッセージの場合。%{value}は実際の属性値に
# 置き換えられる。%{attribute}や%{model}も利用可能。
validates :age, numericality: { message: "%{value}は誤りかもしれません" }
# Procの場合
validates :username,
uniqueness: {
# object = バリデーションされる人物のオブジェクト
# data = { model: "Person", attribute: "Username", value: <username> }
message: ->(object, data) do
"#{object.name}さま、#{data[:value]}は既に入力済みです"
end
}
end
on
オプション
:onオプションは、バリデーション実行のタイミングを指定
します。ビルトインのバリデーションヘルパーは、デフォルトでは保存時(レコードの作成時および更新時の両方)に実行されます。バリデーションのタイミングを変更したい場合、on: :createを指定すればレコード新規作成時にのみ
バリデーションが行われ、on: :updateを指定すればレコードの更新時にのみ
バリデーションが行われます。
class Person < ApplicationRecord
# 値が重複していてもemailを更新できる
validates :email, uniqueness: true, on: :create
# 新規レコード作成時に、数字でない年齢表現を使える
validates :age, numericality: true, on: :update
# デフォルト (作成時と更新時の両方でバリデーションを行なう)
validates :name, presence: true
end
例外を表示させる
バリデーションを厳密にし、オブジェクトが無効だった場合にActiveModel::StrictValidationFailedが発生する
ようにすることもできます。
class Person < ApplicationRecord
validates :name, presence: { strict: true }
end
irb> Person.new.valid?
ActiveModel::StrictValidationFailed: Name can't be blank
カスタムの例外を表示させることができる。
カスタム例外を:strictオプションに追加
することもできます。
class Person < ApplicationRecord
validates :token, presence: true, uniqueness: true, strict: TokenGenerationException
end
irb> Person.new.valid?
TokenGenerationException: Token can't be blank
出典
感想
- errorsオブジェクトを使えるようにする。
- バリデーションをスキップする理由がわかったら使ってみたい。
- procとは?
まだまだ知らないことばかりだ。
わかったら記事にする。
とりあえずここの技術を実際使ってみて実装する
長さや正規表現なんかも調べることができそうだ。
また勉強しているうちに新しくバリデーションのアイデアが浮かんできそうだ。
javascriptと一緒に使えそうだ。