0
0

More than 1 year has passed since last update.

バリデーションを始めて扱う

Posted at

バリデーション(検証)

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と一緒に使えそうだ。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0