【Rails入門】validationsについて解説
はじめに
「データの正しさ」を意識したことはあるでしょうか?
Webアプリケーションは、ユーザーとやりとりして多くの情報を受け取り、それに従って処理を行います。
そのため、受け取るデータが、処理できる形式のものであるかどうかを確認するのは、とても重要です。
特にユーザーが入力するデータは、全角半角や桁数、入力漏れ、入力項目違いなど、入力ミスがある可能性が十分に考えられます。
それらの「間違ったデータ」を最初に見つけて「正しいデータ」にしておくことで、その後の処理を実装するための検討内容を少なくし、余計な手数やバグを防ぐことができるでしょう。
今回は、そんな「データの正しさ」を検証することができるValidationsについて、解説します。
RailsのValidationsを知る
RailsのValidationsは、与えられたデータ(オブジェクト)が、その後の処理に適したものかどうかを確認するための検証処理です。
例えば、ユーザー情報の入力で、電話番号や郵便番号欄に半角の数字とハイフン以外の文字があったり、メールアドレスに全角文字が使われていたりされていると、その後の処理でいちいち変換する必要が出てきます。
そのため、最初の入力時に、適していない入力があった場合にエラー表示するなどして、再入力を求める動作にした方が、効率的です。
そういった「データが最適かどうかのチェックをする」のが、バリデーション(Validations)なのです。
Validationのタイミング
検証を行うタイミングは、それほど多くはありません。
なぜなら、あちこちで検証をするのではなく、できる限り同じタイミングで処理した方が、メンテナンス性や拡張性が高くなるからです。
例えば、ユーザーデータを登録して、それをもとに最適な情報を表示するプログラムを考えてみましょう。
一般的に、以下のようなフローになります。
↓
2.データをデータベースへ登録
↓
3.データベースからユーザーデータを取得
↓
4.取得したユーザーデータを解析
↓
5.解析結果から情報を取得して表示
上記のフローで、検証を行えるタイミングは、次の4カ所です。
・1.の直後
・2.の直前
・3.の直後
・4.の直前
仮に、「1.の直後」に検証処理をしていれば、それ以降は「データが正しい」ことが補償されていますので、以降の処理では検証する必要がありません。逆に言えば、以降の処理は「データの形式は正しい」という前提のもとに作成することができますので、検討するパターンが減り、効率の良い実装ができます。
そのため、「1.の直後」である、ユーザーがデータを入力した直後のタイミングに検証処理を実行して、間違っていれば修正させる(正しいものしか保存できない)ようにするのが、最適解になります。
ただし、もしデータ入力がユーザー入力以外にもある場合は、どうでしょうか? 例えば、他のサービスや事前登録したファイルから取り込むなどというのは、良くあることです。
その場合、1.の処理が数種類存在することになりますので、そのすべてに同じ検証処理を実装するのは効率が良くありません。そのため、そういった場合は、「2.の直前」で検証するのが最適でしょう。「2.の直前」に検証し、正しいデータだけデータベースに登録することで、データベースに登録されているデータが正しいものであると補償されます。そのため、その後の処理はデータが正しいことを前提にして処理を進めれば良いわけです。
しかし、作成したWebアプリケーションの管理下にある特定のデータベース以外からデータを取得する可能性がある場合は、データベースからデータを取得した直後である「3.の直後」に検証しておかなければいけません。
そして、そもそもユーザーデータを解析して表示する処理を外部APIで提供するなど、汎用的なものとするのであれば、「4.の直前」である解析処理の直前に検証することで、正しいデータの場合だけ処理する必要があります。
もちろん、複数箇所で検証を行っても問題はありませんが、明らかに「正しいデータしか来ない」タイミングで検証するのは、作成時、実行時ともに無駄な動きになります。
このように、検証を行うタイミングは、「どこから来てどう渡されていくのか」というデータの流れを整理してから考えなければいけません。
どのタイミングで検証すれば、検証漏れがないのかをしっかりと考慮して、検証処理を使っていきましょう。
基本的な使い方
ここからは、検証を実行するための方法を説明します。
Railsで用意されている検証処理を実行する方法は、データベースへのアクセス直前(2.の直前)の方法と、任意の箇所の2種類が準備されています。
データベースへのアクセス直前
以下のデータベースアクセス用のメソッド(Active Recordオブジェクトのメソッド)では、内部的に検証を実行してからデータベースへの登録などを行います。これは、「2.の直前」のタイミングで検証しているということになります。
メソッド | メソッドの意味 | 検証結果が falseの場合の戻り値 |
---|---|---|
create | 新しいレコードを生成して保存する | オブジェクトそのもの |
create! | createの破壊的メソッド | 例外が発生 |
save | レコードを保存する 既存のレコードを別レコードとして保存する、もしくはnewで生成したレコードを保存する |
false |
save! | saveの破壊的メソッド | 例外が発生 |
update | オブジェクトを変更して保存する | false |
update! | updateの破壊的メソッド | 例外が発生 |
これらのメソッドでは、検証結果がtrue(正しいデータ)の場合だけデータベースへデータを保存(変更)し、false(間違ったデータ)の場合は保存しません。
ただし、以下のように引数として「validate: false」を渡すことで、検証を実行しないようにすることが可能です。(あまり推奨されません)
save(validate: false)
また、以下のメソッドでは検証が実行されませんので、注意が必要です。
メソッド | 説明 |
---|---|
decrement! | カラムのデータを指定された値だけ減ずる(数値データにのみ有効) v2.3.8以前 |
decrement_counter | カラムのデータを-1する(数値データにのみ有効) |
increment! | カラムのデータを指定された値だけ加算する(数値データにのみ有効) v2.3.8以前 |
increment_counter | カラムのデータを+1する(数値データにのみ有効) |
toggle! | trueとfalseを入れ替える |
touch | 更新日時(update_at)を現在時刻に書き換える |
update_all | 指定された条件に一致するレコードをすべて更新する |
update_attribute | 指定したカラムのデータを更新する |
update_column | 指定したカラムのデータを更新する |
update_columns | 複数の指定したカラムのデータを更新する |
update_counters | 指定したカラムを+1もしくは-1する v2.3.8以前 |
任意の場所での実行
前述のように、データベースへのアクセス直前に検証が実行されない場合などもありますので、Railsには、別途、検証を実行させる方法も準備されています。
それが、「valid?メソッド」もしくは「invalid?メソッド」です。
それぞれ、以下のように利用することで、検証した結果がtrueもしくはfalseで返ってきます。その結果を基に、その後の処理をどうするか分岐させれば良いわけです。
ActiveRecordオブジェクト.valid?
ActiveRecordオブジェクト.invalid?
valid?メソッドは、正しいデータだった場合はtrue、適していないデータの場合はfalseを返してきます。invalid?メソッドは、その逆の戻り値が返ってきます。
検証結果の確認
検証を実施した結果、もしエラーが発生していた場合、どのカラムでどんなエラーが発生したのか(エラーメッセージ)を取得することができます。
ActiveRecordオブジェクト.errors
errorsメソッドを利用すると、次のようなエラーが発生したカラムと発生したエラーメッセージのハッシュが返ってきますので、valid?メソッドなどの利用時にメッセージ表示するなど活用できるでしょう。
{ カラム名:[エラーメッセージ] }
検証処理(Validator)の内容
では、ここから具体的な検証処理の内容を説明していきましょう。
Railsには、バリデーションヘルパーと呼ばれる標準で搭載された検証処理があり、多くの場合、その組み合わせだけで必要な検証を行うことができるようになっています。
また、もちろんですが自分で作成することも可能になっていますので、それも合わせて紹介します。
バリデーションヘルパー
Railsに標準搭載されているValidationであるバリデーションヘルパーを紹介します。
ここで紹介するヘルパーは、すべて複数のカラムに対して検証を行えますので、効率的な検証が行えます。また、オプションを利用することで、必要十分なカスタマイズが可能になっています。
バリデーションヘルパーを使用するためには、ActiveRecordのモデル(ApplicationRecordクラスのサブクラス)に、validatesメソッドを使って次のようにして記載します。
class モデル名 < ApplicationRecord
validates :カラム名, バリデーションヘルパー名: true
end
class モデル名 < ApplicationRecord
validates :カラム名, バリデーションヘルパー名: { オプション,,, }
end
現時点(v5.2.1時点)で利用できるバリデーションヘルパーは、以下のようになっています。
バリデーションヘルパー | 検証内容 | 備考 |
---|---|---|
acceptance | フォーム上のチェックボックスがオンになっている | データベース上にカラムがなくても検証可能。もしカラムがある場合は、acceptオプションを設定するか、true設定しておくこと |
validates_associated | 他のモデルと関連付けられており、両方のモデルの検証を行う必要がある場合に使用する | ・両方のモデルに設定すると無限ループに陥るので、必ず片方だけに設定すること ・validatesメソッドを使用せず、以下のように使用する validates_associated :関連付けられたモデル名 |
confirmation | 2つのテキストフィールドが一致している | フォーム上の2つのテキストフィールドを比較するが、フィールド名として片方はデータベース上のカラム名、もう一方は仮想のカラムでカラム名_confirmationとすること カラム名_confirmationがnilの場合は検証されない ・case_sensitiveオプションをfalseにすることで、大文字小文字を区別せずに比較可能 |
exclusion | inオプションで与えられたenumerableオブジェクトにカラムの値が含まれていない | inオプションの代わりに別名のwithinオプションも使用可能 |
format | withオプションで与えられた正規表現とカラムの値がマッチする | |
inclusion | inオプションで与えられたenumerableオブジェクトにカラムの値が含まれている | inオプションの代わりに別名のwithinオプションも使用可能 |
length | カラムの値の文字数がオプションの範囲内 | 以下のオプションを使用可能 ・minimum:指定された値以上 ・maximum:指定された値以下 ・inまたはwithin:指定された範囲内(例:2..20) ・is:指定された値と等しい |
numericality | カラムの値が整数または浮動小数点である | 通常は、カラムがnilの場合、エラーが発生する 以下のオプションを使用可能 ・greater_than:指定された値よりも大きい ・greater_than_or_equal_to:指定された値以上 ・equal_to:指定された値と等しい ・less_than:指定された値よりも小さい ・less_than_or_equal_to:指定された値以下 ・other_than:指定した値以外 ・odd:trueに設定されている場合は、奇数であること ・even:trueに設定されている場合は、偶数であること ・only_integer:trueに設定されている場合は、整数であること |
presence | カラムの値がnilや空文字ではない | 真偽値に対しては、正しい結果が得られない(falseは空文字と判断されるため) 真偽値の存在をチェックする場合は、inclusionを使用すること |
absence | カラムの値がnilや空文字である | 真偽値に対しては、正しい結果が得られない 真偽値の存在をチェックする場合は、exclusionを使用すること |
uniqueness | モデルのテーブルに対して、そのカラムの値と同じ値を持つ既存のレコードがない | ・case_sensitive:falseにすることで、大文字小文字を区別せずに比較可能 ・scope:ここに指定したカラムと組み合わせて検証する |
validates_with | Validationに使用するクラス、クラスのリストを指定する 自作のValidationを利用する場合に使用する |
validatesメソッドを使用せず、以下のように使用する validates_with :クラス名 |
validates_each | 指定したカラムを検証するブロックを設定する | validatesメソッドを使用せず、以下のように使用する
validates_each :カラム名 do |ブロック引数1, ブロック引数2, ブロック引数3 ブロック引数には、ActiveRecordレコードオブジェクト、カラム名、カラムの値が渡される |
これらのバリデーションヘルパーを組み合わせることで、ほとんどの場合のデータ検証を行うことが可能になるでしょう。
検証処理をカスタマイズする共通オプション
また、以下のオプションは上記すべてで使用できるものです。これらを利用することで、より柔軟な検証が可能になります。(ヘルパー特有のオプションは、上記の備考欄を参照してください)
オプション | 内容 |
---|---|
allow_nil | カラムの値がnilの場合、検証をスキップする |
allow_blank | カラムの値がnilと空文字(blank?がtrue)の場合、検証をスキップする |
message | デフォルト設定以外のエラーメッセージを登録する メッセージには、%{value}、%{attribute}、%{model}を含めることができるが、この通りの記載にすること(空白を入れるのも不可) |
on | 検証が自動実行される場合の実行タイミングを指定する 例):createを指定すれば、レコードの新規作成時のみ検証される |
strict | 検証に失敗した場合、例外(ActiveModel::StrictValidationFailed)を発生させる |
if | 特定の条件のときのみ、検証を実行する |
unless | 特定の条件出ないときに、検証を実行する |
カスタムバリデータ(オリジナルのValidation)を作る
前述で紹介したバリデーションヘルパーでは、どうしても実現できない検証を行いたい場合もあります。そんな場合は、自作のValidationを作ることも可能です。
なお、カスタムバリデータの実行には、すでに紹介しているバリデーションヘルパー「validates_with」を使ってレコードを渡す必要がありますので、呼び出し方法も含めて紹介しましょう。
カスタムバリデータの定義(検証対象のカラム名が決まっている場合)
特定のカラムを検証するカスタムバリデータを作成する場合は、ActiveModel::Validatorを継承するクラスを定義し、そこにvalidateメソッドを実装します。
基本的には以下のようになるでしょう。
# カスタムバリデータの追加
class バリデータ名 < ActiveModel::Validator
def validate(ActiveRecordオブジェクト)
unless ActiveRecordオブジェクト.カラム名を使った条件
record.errors[カラム名] << ‘エラーメッセージ’ # エラーメッセージを追加する
end
end
end
#モデルでの呼び出し
class モデル名 < ApplicationRecord
include ActiveModel::Validations
validates_with バリデータ名
end
カラム名が決まっている専用バリデータのため、シンプルなプログラムコードになります。ただし、この方法は、汎用的なカラムの検証を行うのに不向きです。
また、この方法で定義されたバリデータは、最初の実行時に初期化された後、初期化されることはありません。更新されるインスタンス変数を使った処理を行っている場合は、実行の都度、明示的に初期化処理を行うなど、注意して扱わなければいけません。
カスタムバリデータの定義(検証対象のカラム名が不定な場合)
Railsには、カラム名を引数として渡すことで、汎用的に利用できるカスタムバリデータを作る方法も提供されています。
そのためには、ActiveModel::EachValidatorを継承するクラスを定義して、validate_eachメソッドを実装しましょう。このとき、validate_eachメソッドの引数として、(ActiveRecordオブジェクト、カラム名、カラムの値)が渡されますので、呼び出し側が任意の対象を設定できるわけです。
一般的には、以下のような定義と使用方法になります。
# カスタムバリデータの追加
class バリデータ名Validator < ActiveModel::EachValidator
def validate_each(ActiveRecordオブジェクト,カラム名,カラムの値)
unless カラムの値を使った条件
record.errors[カラム名] << ‘エラーメッセージ’ # エラーメッセージを追加する
end
end
end
#モデルでの呼び出し
class モデル名 < ApplicationRecord
validates :カラム名, バリデータ名: true, バリデーションヘルパー名: true,,, # バリデーションヘルパーを同時に設定することも可能<span class=”red”>(1)</span>
end
※(1)で「バリデータ名」を記載することで、定義している「バリデータ名Validator」を利用することを宣言することになります。例えば、「NameValidator」の場合は「name」になります。
カスタムバリデータを別ファイルで定義した場合
ほとんどの場合、カスタムバリデータをモデル内で定義することはなく、カスタムバリデータ用のディレクトリを作って、別のファイルに定義することでしょう。その場合は、定義したファイルを読み込んでやる必要がありますので、注意が必要です。
カスタムバリデータを定義したファイルが、「/app/validators」というディレクトリに格納されている場合、「config/application.rb」に追記します。
module Core
class Application < Rails::Application
:
:
config.autoload_paths += Dir[“#{config.root}/app/validators”] # カスタムバリデータを自動で読み込むようにする
:
:
end
end
以上の追記を行うことで、アプリケーション起動時にカスタムバリデータが読み込まれます。
まとめ
RailsのValidationsについて、解説しました。
データの整合性を保障するValidationは、Railsのプログラムの効率を上げ、思わぬバグを防ぐのに、非常に効果があります。
ただし、どんなタイミングで検証するかによって、検証漏れや余計な検証を行うことがありますし、メンテナンス性が悪くなります。
Validationのタイミングを考えるには、Railsアプリケーション全体の処理の流れやデータがどこからきてどう利用されるかをしっかりと考慮しなければいけません。
しかし、それらを考慮できるということは、アプリケーション全体を把握できているということです。そうなれば、Railsプログラマー中級者への扉が開いたと思って間違いありません。
・RailsのValidationsは、与えられたデータ(オブジェクト)が、その後の処理に適したものかどうかを確認するための処理
・データが入力された直後がもっとも効率の良い検証タイミング
・検証タイミングは、データがどこからきてどう処理されるのか、処理をどこまで再利用するのかを考慮する必要がある
・標準搭載されている検証処理をバリデーションヘルパーと呼ぶ