【Rails入門】validationsについて解説

公開日: 2018.09.25
更新日: 2024.01.29
Railsのdeviseを用いてログイン認証を実装する方法を解説

はじめに

「データの正しさ」を意識したことはあるでしょうか?

Webアプリケーションは、ユーザーとやりとりして多くの情報を受け取り、それに従って処理を行います。

そのため、受け取るデータが、処理できる形式のものであるかどうかを確認するのは、とても重要です。

特にユーザーが入力するデータは、全角半角や桁数、入力漏れ、入力項目違いなど、入力ミスがある可能性が十分に考えられます。

それらの「間違ったデータ」を最初に見つけて「正しいデータ」にしておくことで、その後の処理を実装するための検討内容を少なくし、余計な手数やバグを防ぐことができるでしょう。

今回は、そんな「データの正しさ」を検証することができるValidationsについて、解説します。

RailsのValidationsを知る

RailsのValidationsは、与えられたデータ(オブジェクト)が、その後の処理に適したものかどうかを確認するための検証処理です。

例えば、ユーザー情報の入力で、電話番号や郵便番号欄に半角の数字とハイフン以外の文字があったり、メールアドレスに全角文字が使われていたりされていると、その後の処理でいちいち変換する必要が出てきます。

そのため、最初の入力時に、適していない入力があった場合にエラー表示するなどして、再入力を求める動作にした方が、効率的です。

そういった「データが最適かどうかのチェックをする」のが、バリデーション(Validations)なのです。

Validationのタイミング

検証を行うタイミングは、それほど多くはありません。

なぜなら、あちこちで検証をするのではなく、できる限り同じタイミングで処理した方が、メンテナンス性や拡張性が高くなるからです。

例えば、ユーザーデータを登録して、それをもとに最適な情報を表示するプログラムを考えてみましょう。

一般的に、以下のようなフローになります。

 1.ユーザーフォームへのデータ入力(ユーザーが手で入力)

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
検証処理と検証失敗時のエラーメッセージの追加処理
end

ブロック引数には、ActiveRecordレコードオブジェクト、カラム名、カラムの値が渡される
エラーメッセージの追加は、ActiveRecordオブジェクト.errors.addメソッドを使用する

これらのバリデーションヘルパーを組み合わせることで、ほとんどの場合のデータ検証を行うことが可能になるでしょう。

検証処理をカスタマイズする共通オプション

また、以下のオプションは上記すべてで使用できるものです。これらを利用することで、より柔軟な検証が可能になります。(ヘルパー特有のオプションは、上記の備考欄を参照してください)

オプション 内容
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は、与えられたデータ(オブジェクト)が、その後の処理に適したものかどうかを確認するための処理
・データが入力された直後がもっとも効率の良い検証タイミング
・検証タイミングは、データがどこからきてどう処理されるのか、処理をどこまで再利用するのかを考慮する必要がある
・標準搭載されている検証処理をバリデーションヘルパーと呼ぶ

関連記事

資料請求

  • 短期集中で最速エンジニア転職を実現-転職成功者インタビュー一覧

    DMM WEBCAMPでは転職成功率98%を実現しています。本資料では、元警察官や元ラーメン屋など様々なバックグラウンドを持つ卒業生の声をお届けします。

    資料をダウンロードする
  • IT技術がもたらす3つの変化と身につけるべきスキル

    IT技術の発展により、今後10~20年程度で47%の仕事がなくなると言われています。どのような変化が訪れ、私達はどのようなスキルを身につけるべきかを解説します。

    資料をダウンロードする
  • 未経験がフリーランスエンジニアになる方法-年収アップで自由な働き方を手に入れる

    働き方改革やリモートワークの影響でフリーランスという働き方の人気は高まりつつあります。フリーランスエンジニアとして活躍するために必要な情報をお届けします。

    資料をダウンロードする

© 2024 WEBCAMP MEDIA Powered by AFFINGER5