更新日:
【Rails】 Railsのバリデーションの使い方をマスターしよう!
バリデーションとは、データベースに保存する前に保存する内容を検証する機能です。
投稿フォームで何かを投稿するとき、「入力必須項目」というのを目にしたことはないでしょうか?
例えば「名前やメールアドレス、パスワード」などです。このとき、入力必須項目に何も入力しなかった場合、投稿ができないようにしたいですね。
他にもパスワードなどは4文字以上であるとか、すでにデータベースに存在しているメールアドレスで登録できないようにするとかデータを保存する前に検証したいときもあります。
そんなときに定義するのがバリデーションです。
バリデーションを定義すると、データベースに保存する際、投稿された内容を検証し、保存するかどうかをチェックできるようになります。
バリデーションの使い方
この章では、バリデーションの使い方について解説します。
バリデーションのトリガ
バリデーションを定義すると、下記のメソッドが動く前に必ず検証が行われます。
- save
- save!
- create
- create!
- update
- update!
save
とupdate
は検証でデータが保存されない場合はfalse
を返します。
1
2
3
4
5
6
[1] pry(<UsersController>)> @user.save
(0.2ms) BEGIN
↳ (pry):1
(0.2ms) ROLLBACK
↳ (pry):1
=> false
1
2
3
4
5
6
[1] pry(<UsersController>)> @user.update(user_params)
(0.2ms) BEGIN
↳ (pry):1
(0.2ms) ROLLBACK
↳ (pry):1
=> false
create
は保存されてもされなくてもオブジェクト自身を返します。
1
2
3
4
5
6
7
8
9
10
[1] pry(<UsersController>)> User.create(name: "")
(0.2ms) BEGIN
↳ (pry):1
(0.4ms) ROLLBACK
↳ (pry):1
=> <User:0x007fd8bf0d0d28
id: nil,
name: "",
created_at: nil,
updated_at: nil>
またそれぞれのメソッド名の最後に!
をつけると保存されなかった場合に例外処理を返します。
つまりfalseやインスタンスが返ってくるのではなく、エラー文が表示されます。
なんで保存がされないのか疑問に思うときは!
を付けてエラーの原因を探ってみましょう。
1
2
3
4
5
6
7
8
9
10
11
12
[1] pry(<UsersController>)> user = User.create(name: "")
=> <User:0x007fd8bf0d0d28
id: nil,
name: "",
created_at: nil,
updated_at: nil>
[2] pryy(<UsersController>)> user.save!
(0.2ms) BEGIN
User Exists? (0.2ms) SELECT 1 AS one FROM `users` WHERE `users`.`name` IS NULL LIMIT 1
(0.2ms) ROLLBACK
ActiveRecord::RecordInvalid: バリデーションに失敗しました: Nameを入力してください
# 上のメッセージが表示される
バリデーションを定義してみよう
それではバリデーションを定義してみましょう。
モデルに記入
バリデーションを定義するには、下記のようにモデルに記述します。
1
validates :カラム名, ヘルパー
ヘルパーを使うとどういう検証を行うのかを定義することができます。
ヘルパーの部分は後述します。
下記のように同時に複数のカラムを指定することもできます。
1
validates :カラム名, :カラム名, :カラム名, ヘルパー
バリデーションのヘルパー
railsにはバリデーションのヘルパーが多数用意されています。
このヘルパーを使うとどういう検証を行うかを定義することができます。
主なヘルパーを紹介します。
presence
一番多く使うヘルパーです。
定義すると「空でないか」を検証します。
1
validates :カラム名, presence: true
下記のように記述するとnameカラムにちゃんと値が入っているかを検証します。
1
validates :name, presence: true
なのでこのカラムに何も入っていないときは保存されません。
上のコードはvalidates_presence_of
を使って書くこともできます。
1
validates_presence_of :name
absence
presence
とは逆で定義すると「空であるか」を検証します。
1
validates :カラム名, absence: true
上のコードはvalidates_absence_of
を使って書くこともできます。
1
validates_absence_of :カラム名
uniqueness
値が一意(unique)であり重複していないかを検証します。
メールアドレスなど重複しては困るときに使います。
1
validates :カラム名, uniqueness: true
このように定義するとカラムにすでに存在している内容と同じものがあるかどうかを検証することができます。
すでに値が存在しているものと同じ値であれば保存されません。
上のコードはvalidates_uniqueness_of
を使って書くこともできます。
1
validates_uniqueness_of :カラム名
また下記のオプションがあります。
オプション | 役割 |
---|---|
scope | 範囲を限定する別の属性を指定 |
case_sensitive | 大文字小文字を区別する |
scopeオプション
一意性チェックの範囲を限定する別の属性を指定するオプションです。
1
2
3
4
class Tag < ApplicationRecord
belongs_to :user
validates :name, uniqueness: { scope: :user }
end
上のように定義すると一人のユーザーがタグの名前に同じものを使えないという意味になります。
scopeオプションをつけないと全てのユーザーが同じ名前のものを使えなくなってしまいます。
つまり他人がつけた名前は自分も使えなくなりますが、scopeオプションを使えば、他人がつけていても自分がつけていなければ名前をつけられるようになります。
case_sensitiveオプション
一意性チェックをするときに大文字か小文字を区別するかどうかを指定できるオプションです。
デフォルトはtrueになっているので区別をします。
区別したくない時はfalseとしましょう。
1
2
3
class User < ApplicationRecord
validates :name, uniqueness: { case_sensitive: false }
end
acceptance
チェックボックスがオンになっているかどうかを検証します。
サービスに対する利用条項を読んで、「同意した」などのチェックボックスがあるサイトをよく見かけるかと思います。
そのときチェックボックスにチェックが入っているかを検証するときなどに利用します。
1
validates :カラム名, acceptance: true
上のコードはvalidates_acceptance_of
を使って書くこともできます。
1
validates_acceptance_of :カラム名
confirmation
2つのフォームで入力された内容が完全に一致するかを検証します。
メールアドレスのフォームとメールアドレスの確認フォームのが全く同じであるか検証したいときなどに利用します。
1
validates :カラム名, confirmation: true
一致しているか確認したいもう一つのカラム名は末尾に_confirmation
をつけます。
こちらはフォームでのみ使うカラムなのでデータベースにこのカラムを作成する必要はありません。
メールアドレスを例にすると下記のようになります。
1
validates :email, confirmation: true
このように記述するとemailカラム
とemail_confirmationカラム
の入力値が一致しているかを検証します。
上のコードはvalidates_confirmation_of
を使って書くこともできます。
1
validates_confirmation_of :email
inclusion
値が指定した文字になっているかを検証します。
基本的にinオプションと一緒に使用します。
1
2
3
4
5
# 1つのワードの指定
validates :カラム名, inclusion: { in: ["検証したい文字"] }
# 複数のワードの指定
validates :カラム名, inclusion: { in: ["検証したい文字1", "検証したい文字2"] }
上の複数のワードの指定時のコードは下記のように%記法を使って書くこともできます。
1
validates :カラム名, inclusion: { in: %w(検証したい文字1 検証したい文字2) }
数字の1から100まで以外は保存されないようにするには下記のように記述します。
1
validates :size, inclusion: { in: 1..100 }
上のコードはvalidates_inclusion_of
を使って書くこともできます。
1
validates_inclusion_of :size, in: 1..100
他にも例えば文字の大きさを入力するフォームがあったとき、必ず「大」、「中」、「小」のいずれかの文字にしてもらいたい場合があるとします。
その時は下記のように指定すればこの3文字以外の文字が入力された時には保存がされないようにすることができます。
1
validates :size, inclusion: { in: %w(大 中 小) }
この時はたとえ「特大」のように文字の中に「大」が入っていたとしても「大」か「中」か「小」という文字でなければ保存されません。
このように特定の文字だけを保存したい時に使用します。
exclusion
指定された値が含まれていないかを検証します。
基本的にinオプションと一緒に使用します。
1
validates :カラム名, exclusion: { in: ["検証したい文字1", "検証したい文字2"] }
上のコードは下記のように%記法を使って書くこともできます。
1
validates :カラム名, exclusion: { in: %w(検証したい文字1 検証したい文字2) }
保存したくない文字を指定する時に使います。
例えばユーザー名に「管理人」とつけて欲しくない場合は下記のように指定します。
1
validates :name, exclusion: { in: %w(管理人) }
このようにすると「管理人」と入力された場合は保存されないようにすることができます。
「管理人A 」の場合は保存されます。
上のコードはvalidates_exclusion_of
を使って書くこともできます。
1
validates_exclusion_of :name, in: %w(管理人)
length
値の長さを検証します。
下記のオプションを使って定義します。
オプション名 | 内容 |
---|---|
minimum | 最小値を指定します |
maximum | 最大値を指定します |
in | 長さの範囲を指定します |
is | 値の長さを指定します |
具体的には下記のように使用します。
1
2
3
4
validates :カラム名, length: { minimum: 2 } #最低でも2文字以上であるか
validates :カラム名, length: { maximum: 6 } #6文字以内であるか
validates :カラム名, length: { in: 5..10 } #5文字から10文字以内であるか
validates :カラム名, length: { is: 5 } #5文字であるか
上のコードはvalidates_length_of
を使って書くこともできます。
1
validates_length_of :first_name, maximum: 6
format
正規表現と属性の値が合致するかの検証をします。
withオプション
と併用して使います。
1
validates :カラム名, format: { with: 正規表現 }
下記のように正規表現で指定します。
1
validates :name, format: { with: /\A[a-zA-Z]+\z/ }
上のコードは半角英文字だけ入力を許可することを意味します。
なので「pikawaka」は保存され、「ピカわか」だと保存されません。
上のコードはvalidates_format_of
を使って書くこともできます。
1
validates_format_of :name, with: /\A[a-zA-Z]+\z/
よく使う正規表現をまとめてみます。
用途に書いてある書式だけ入力可能です。
※「全角ひらがな・カタカナ」であれば全角のひらがなとカタカナどちらかだけ入力可能
用途 | 正規表現 |
---|---|
全角ひらがな | /\p{hiragana}/ /\A[ぁ-んー-]+\z/ |
全角カタカナ | /\p{katakana}/ /\A[ァ-ヶー-]+\z/ |
半角カタカナ | /\A[ァ-ン゙゚]+\z/ |
全角ひらがな・カタカナ | /\A[ぁ-んァ-ヶー-]+\z/ |
漢字 | /\A[一-龥]+\z/ |
全角ひらがな・漢字 | /\A[ぁ-ん一-龥]/+\z |
全角ひらがな・カタカナ・漢字 | /\A[ぁ-んァ-ヶ一-龥]/+\z |
半角英文字(小文字) | /\A[a-z]+\z/ |
半角英文字(大文字) | /\A[A-Z]+\z/ |
半角英文字(全て) | /\A[a-zA-Z]+\z/ /\A[a-z]+\z/i |
全角数値 | /\A[0-9]+\z/ |
半角数値 | /\A[0-9]+\z/ |
半角小文字英文字・数値 | /\A[a-z0-9]+\z/ |
半角大文字英文字・数値 | /\A[A-Z0-9]+\z/ |
半角英文字・数値 | /\A[a-zA-Z0-9]+\z/ /\A[a-z0-9]+\z/i |
半角英数字を両方含む | /\A(?=.*?[a-z])(?=.*?\d)[a-z\d]+\z/i |
郵便番号(ハイフンあり7桁) | /\A\d{3}[-]\d{4}\z/ |
郵便番号(ハイフンなし7桁) | /\A\d{7}\z/ |
/\A([^@\s]+)@((?:[-a-z0-9]+.)+[a-z]{2,})\z/i /\A[\w+-.]+@[a-z\d-]+(.[a-z\d-]+)*.[a-z]+\z/i など |
|
電話番号(ハイフンなし10・11桁) | /\A\d{10,11}\z/ |
例えばパスワードに半角英数字を両方含めなければいけないときは下記のように記述します。
1
validates :password, format: { with: /\A(?=.*?[a-z])(?=.*?\d)[a-z\d]+\z/i }
numericality
属性に数値のみが使われているかを検証します。
1
validates :カラム名, numericality: true
オプションとして以下の制約を指定することができます。
オプション | 制約 |
---|---|
:only_integer | trueにすると整数のみを指定 |
:greater_than | 指定された値よりも大きくなければならないことを指定 |
:greater_than_or_equal_to | 指定された値と等しいか、それよりも大きくなければならないことを指定 |
:equal_to | 指定された値と等しくなければならないことを指定 |
:less_than | 指定された値よりも小さくなければならないことを指定 |
:less_than_or_equal_to | 指定された値と等しいか、それよりも小さくなければならないことを指定 |
:other_than | 渡した値以外の値でなければならないことを指定 |
:odd | trueにすると奇数であるかを指定 |
:even | trueにすると偶数であるかを指定 |
例えば整数のみを許可する場合は下記のように記述します。
1
validates :price, numericality: { only_integer: true }
100以上1,000以下の場合は下記のように記述します。
1
validates :price, numericality: { greater_than_or_equal_to: 100, less_than_or_equal_to: 1_000 }
上のコードはvalidates_numericality_of
を使って書くこともできます。
1
validates_numericality_of :price, greater_than_or_equal_to: 100, less_than_or_equal_to: 1_000
バリデーションのオプション
オプションには全てのヘルパーで使うことができるものがあります。
on
検証が行われるタイミングを指定することができます。
1
validates :カラム名, ヘルパー, on: :メソッド名
下のように記述するとcreateメソッドが実行されたときにだけ一意かどうかの検証が行われます。
1
validates :email, uniqueness: true, on: :create
saveメソッドはcontextパラメータでon: :hoge
のhoge
の部分を指定することができます。
1
2
3
4
5
6
# モデル
validates :email, uniqueness: true, on: :create_account
# コントローラー
@user.save(context: :create_account)
# 独自に作成したcreate_acountというメソッドが実行された時にだけ検証が行われる
例えばユーザー登録の時はemailと名前だけ保存されれば良いけれど、その後、商品を購入したい時にはユーザー編集ページでユーザー情報に住所を入力する必要があるとします。
その際には下記のようにすれば更新する時にだけ住所が入力されているかを検証させることができます。
1
2
3
4
5
6
# emailとnameは作成時と更新時どちらも検証が行われる
validates :email, presence: true
validates :name, presence: true
# addressは更新時のみ検証が行われる
validates :address, presence: true, on: :update
message
バリデーションが失敗した時に全てのエラーメッセージが入っているerrorsコレクション
という場所に自分で作成したカスタムエラーメッセージを追加することができるオプションです。
指定をしていない場合はデフォルトで用意されているメッセージが表示されます。
1
validates :カラム名, ヘルパー: { message: "出力されるメッセージ" }
下記のように記述するとnameカラムがnilだった場合のエラーメッセージが「名前を入力してください」となります。
1
validates :name, presence: { message: "名前を入力してください" }
エラーメッセージとは何かについては後述するerrorsメソッド
を参照してください。
allow_nil
値がnilの場合、検証を行わなくすることができます。
1
validates :カラム名, ヘルパー, allow_nil: true
何も入力しないときは検証したくない場合は下記のように記述します。
1
validates :name, length: { minimum: 2 }, allow_nil: true
allow_blank
nilや空文字など値がblank?
に該当する場合、検証を行わなくすることができます。
上のallow_nil
と似ていますが、allow_nil
は空文字を入力した場合(フォームに何も入力しなかった場合)は検証をしてしまいますが、allow_blank
だと検証をスキップさせることができます。
1
2
3
4
5
6
7
# この場合空文字を入力するとバリデーションの検証が実行される
# つまり1文字以下の場合は検証が行われROLLBACKされる
validates :name, length: { minimum: 2 }, allow_nil: true
# この場合空文字を入力するとバリデーションの検証はスキップされる
# つまり1文字以下の場合の検証がスキップされるので保存される
validates :name, length: { minimum: 2 }, allow_blank: true
便利なメソッド
その他の便利なメソッドを紹介します。
valid?
検証をするメソッドです。
検証は上で紹介したメソッドが実行される前に行われますが、このメソッドを使用しても検証が実行されます。
例えばnewメソッドは実行時には検証が行われないので、newされた時に保存されるかを確認したい時に使用します。
もし保存できる状態であればtrue
が、検証で引っかかって保存できない状態であればfalse
が返ります。
1
validates :text, presence: true
1
2
3
4
5
6
7
8
9
[1] pry(main)> article = Article.new(text: "")
=> #<Article:0x007fc5b3b6d6d8
id: nil,
text: "",
created_at: nil,
updated_at: nil,
user_id: nil>
[2] pry(main)> article.valid?
=> false
詳しい使い方については後述します。
invalid?
valid?メソッド
の逆の返り値を返すメソッドです。
保存できる状態であればfalse
が、検証で引っかかって保存できない状態であればtrue
が返ります。
errors
valid?メソッド
を使った後にerrorsメソッド
を使うと今起きているエラーをエラーメッセージとして確認することができます。
1
validates :text, presence: true
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
[1] pry(main)> article = Article.new(text: "")
=> #<Article:0x007fc5b3b6d6d8
id: nil,
text: "",
created_at: nil,
updated_at: nil,
user_id: nil>
[2] pry(main)> article.valid?
=> false
[3] pry(main)> article.errors
=> #<ActiveModel::Errors:0x007fc5b85158f8
@base=
#<Article:0x007fc5b3b6d6d8
id: nil,
text: "",
created_at: nil,
updated_at: nil,
user_id: nil>,
@messages={:text=>["can't be blank"]}>
最後の行がエラーメッセージです。
ここではcan't be blank
、つまり「空白にはできません」という原因で保存されていないのがわかります。
このようにどのような検証が行われて保存できないかをエラーコードで確認することができます。
errors[:カラム名]
カラムを指定してエラーメッセージを確認することができます。
1
validates :text, presence: true
1
2
3
4
5
6
7
8
9
10
11
[1] pry(main)> article = Article.new(text: "")
=> #<Article:0x007fc5b3b6d6d8
id: nil,
text: "",
created_at: nil,
updated_at: nil,
user_id: nil>
[2] pry(main)> article.valid?
=> false
[3] pry(main)> article.errors[:text]
=> ["can't be blank"]
errors.messages
カラムごとのエラーメッセージを一度に確認することができます。
1
validates :text, presence: true
1
2
3
4
5
6
7
8
9
10
11
12
[1] pry(main)> article = Article.new(title: "hoge", text: "")
=> #<Article:0x007fc5b3b6d6d8
id: nil,
title: "hoge",
text: "",
created_at: nil,
updated_at: nil,
user_id: nil>
[2] pry(main)> article.valid?
=> false
[3] pry(main)> article.errors.messages
=> {:text=>["can't be blank"]}
errors.full_messages
詳細なエラーメッセージを確認することができます。
1
validates :text, presence: true
1
2
3
4
5
6
7
8
9
10
11
[1] pry(main)> article = Article.new(text: "")
=> #<Article:0x007fc5b3b6d6d8
id: nil,
text: "",
created_at: nil,
updated_at: nil,
user_id: nil>
[2] pry(main)> article.valid?
=> false
[3] pry(main)> article.errors.full_messages
=> ["Text can't be blank"]
バリデーションに条件をつけよう
オプションを使うと条件付きで検証を行うことができます。
:if
特定の条件の時に検証を行う必要がある時に使用するオプションです。
1
validates :カラム名, ヘルパー, if: :条件式やメソッド
deviseで定義されているuser_signed_in?
というヘルパーメソッドを使ってユーザーがログインしている時だけ検証をすることができたりします。
1
validates :text, presence: true, if: :user_signed_in?
:unless
ifオプションと同じで特定の条件の時に検証を行う必要がある時に使用するオプションです。
ifとは逆で条件式がfalse
の時にだけ検証をすることができます。
with_options
1つの条件を複数のバリデーションで使いたい時に使用します。
1
2
3
4
with_options ヘルパー do
validates :カラム名, ヘルパー
validates :カラム名, ヘルパー
end
このように指定します。
1
2
3
4
with_options presence: true, format: { with:/\A[ァ-ヶー-]+\z/, message: '全角カタカナで入力してください' } do
validates :first_name_kana
validates :last_name_kana
end
上のように記述するとfirst_name_kanaカラムとlast_name_kanaカラムの両方に値が存在しなければならないことと、全角カタカナで入力をしないといけない制限を一括でかけることができます。
バリデーションの流れ
検証された結果、入力必須なのに値がなかったので保存時にROLLBACKがかかり、保存されません。
1
2
3
4
User Load (0.4ms) SELECT `users`.* FROM `users` WHERE `users`.`id` = 1 ORDER BY `users`.`id` ASC LIMIT 1
User Load (0.8ms) SELECT `users`.* FROM `users`
(0.1ms) BEGIN
(5.5ms) ROLLBACK
このようにターミナルのログを確認するとバリデーションに引っかかって、保存されないことが確認できます。
予期しないエラーが起きてdebugする場合
データが保存される際、こちらが予期しない理由で検証が行われ、データが保存されずROLLBACKされてしまう場合があります。
この時にどういう理由でROLLBACKされてしまうのか原因を探る必要があります。
その際に使うメソッドが先ほど紹介したvalid?メソッドです。
今回は投稿フォームにちゃんと登録をしたのにも関わらず登録ができない時の例をみてみましょう。
ちゃんと値を入力しているのに保存ができない理由がなぜだかわかりません。
1
2
3
4
5
6
7
8
Processing by UsersController#create as HTML
Parameters: {
# 中略
"user"=>{"name"=>"管理人", "sex"=>"男", "age"=>"55", "tall"=>"167", "weight"=>"45"}, "commit"=>"送信"}
(0.9ms) BEGIN
↳ app/controllers/users_controller.rb:11
(0.2ms) ROLLBACK
↳ app/controllers/users_controller.rb:11
このように予期せぬエラーが起きたときにどうやってデバッグするか、エラーの原因を調査する流れを説明していきます。
今回のモデルの記述は下記のようになっています。
1
2
3
4
class User < ApplicationRecord
validates :name, presence: true
belongs_to :job
end
バリデーションはnameカラムが入力必須ということしか定義していません。
今回はちゃんとnameカラムに値が入力されているのになぜか検証で引っかかってしまいました。
理由がわからないので、まずはcreateアクション内でbinding.pryを使い処理を止めます。
次に保存するインスタンスにvalid?メソッド
で検証が行われて保存できる状態か、できない状態かを調べます。
返り値がfalse
なので検証で保存できない状態だということが確認できました。
1
2
[1] pry(main)> @user.valid?
=> false
valid?メソッド
を使用するとそのインスタンスがerrorsメソッド
を使用した時にエラーメッセージを確認することができるようになります。
では実際どんなエラーメッセージが出ているか確認してみます。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[2] pry(main)> @user.errors
=> #<ActiveModel::Errors:0x007fdcb2db3620
@base=
#<User:0x007fd522d08870
id: nil,
name: "管理人",
job_id: nil,
sex: "男",
age: 55,
tall: 167.0,
weight: 45.0,
created_at: nil,
updated_at: nil>,
@details={:job=>[{:error=>:blank}]},
@messages={:job=>["must exist"]}>
このようにmust exist
というメッセージが確認できました。
このメッセージは「jobカラムの値が存在しなければならない」という意味です。
今回は存在しないjobカラムに対するエラーが表示されました。
色々調べるとrails5からアソシエーションを定義しているとusersテーブルとjobsテーブルとの結びつけをするjob_id
というカラムにデフォルトで入力必須のバリデーションが定義されてしまうためだということがわかりました。
この場合アソシエーションの定義であるbelongs_to
のバリデーションに引っかかるとjob_id
ではなく、モデル名であるjob
がエラーメッセージ内に表示されるようです。
外部キーのバリデーション
Rails4まではアソシエーションを定義した時のbelongs_to
の外部キーでnilを許可しないようにbelongs_to :user, required: true
などのバリデーションを手動で定義していました。
ですが、rails5からは自動でnilを許可しないバリデーションが定義されるようになりました。
これを意図的に外すには下記のように定義します。
1
belongs_to :user, optional: true
このように自動でnilを許可しないバリデーションが定義されているので、投稿フォームに職業を登録するフォームを追加し、job_id
も保存できるようにすることによって今回のエラーを解決することができました。
このようにvalid?メソッド
とerrorsメソッド
を使用することで原因を特定することができます。
もしvalid?メソッド
を使用していないインスタンスにerrorsメソッド
を使用すると下記のようにmessageの部分は何も入っていない状態になってしまい、エラーメッセージが確認できません。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
=> #<ActiveModel::Errors:0x007fcb3ceb9c68
@base=
#<User:0x007fcb3e594848
id: nil,
name: "ピカわか",
job_id: 5,
sex: 0,
age: 25,
tall: 170.0,
weight: 85.0,
created_at: nil,
updated_at: nil>,
@details={},
@messages={}>
もし原因不明でROLLBACKが起きて値を保存できない場合は、valid?メソッド
とerrorsメソッド
を利用して原因を確認してみましょう。
意図的にエラーを起こす場合
データを保存する際、保存できた時とできなかった時で処理を分けたい場合があります。
データを保存するメソッドにcreateとsaveメソッドがありますが、saveメソッドを使うと検証で引っかかった時にfalse
が返ります。
これはvalid?メソッド
を使った時と同じことをsaveメソッドが行なっているからです。
1
2
3
4
5
6
7
8
9
10
11
[1] pry(main)> article = Article.new(text: "")
=> #<Article:0x007fc5b8079830
id: nil,
text: "",
created_at: nil,
updated_at: nil,
user_id: nil>
[2] pry(main)> article.save
(132.4ms) BEGIN
(8.2ms) ROLLBACK
=> false
valid?メソッド
を使った時と同じことをsaveメソッドが行なっているということはsaveメソッドを使っても、errorsメソッド
を使用した時にエラーメッセージを確認することができます。
1
2
3
4
5
6
7
8
9
10
[3] pry(main)> article.errors
=> #<ActiveModel::Errors:0x007fc5b8020118
@base=
#<Article:0x007fc5b8079830
id: nil,
text: "",
created_at: nil,
updated_at: nil,
user_id: nil>,
@messages={:text=>["can't be blank"]}>
そして検証で引っかかるとfalse
が返るのでコントローラーでは下記のように条件分岐をさせることができます。
1
2
3
4
5
6
7
8
def create
@article = Article.new(article_params)
if @article.save
redirect_to articles_path
else
render :new
end
end
ここでは保存がされればルートパスへ、保存が失敗すればもう一度投稿フォームが表示されるよう記述しています。
またデータベースに保存されなかった時に、ユーザーにエラーが出て保存がされなかったことをメッセージで知らせることができます。
その時に使うメソッドがerrors.any?メソッドです。
もしエラーが出て保存されない場合、返り値としてtrue
が返ります。
それを利用してビューファイルには下記のように記述をします。
1
2
3
<% if @article.errors.any? %>
<p class="red">名前を入力してください</p>
<% end %>
こうすることにより、ユーザーに保存ができなかったことを知らせることができます。
カスタムバリデーションを作ろう
今まで紹介してきた中に自分が実行したいバリデーションがない場合は自分で条件を作成することができます。
そのことをカスタムバリデーションと呼びます。
では実際に自分でバリデーションを定義する流れをみていきましょう。
まずはappフォルダ内にvalidatorsフォルダ
を作成します。
validatorsフォルダ内にはカスタムバリデーションを定義するファイルを作成します。
この時のファイル名はバリデーション名_validator.rb
にします。
例えば下記のようにNameCheck
という名前でバリデーションを作成した時のファイル名は
name_check_validator.rb
となります。
1
2
3
class NameCheckValidator < ActiveModel::EachValidator
# バリデーションの内容
end
次にそのファイルに下記のようなコードを記述します。
1
2
3
4
5
class バリデーション名Validator < ActiveModel::EachValidator
def validate_each(record, attribute, value)
検証する内容
end
end
カスタムバリデーションを作成する際にはvalidate_eachメソッドを使用します。
3つの引数は順番に(保存する前のテーブルのレコード, カラム名, 送信した値)を表します。
実際には下記のように記述します。
1
2
3
4
5
6
7
class NameCheckValidator < ActiveModel::EachValidator
def validate_each(record, attribute, value)
if value == "管理人"
record.errors[attribute] << "その名前は使用できません"
end
end
end
次にこのバリデーションを有効にするためconfig/application.rb
にカスタムバリデーションを定義したファイルがあるフォルダのパスを下記のように追加します。
1
2
3
4
5
module アプリ名
class Application < Rails::Application
config.autoload_paths += Dir["#{config.root}/app/validators"]
end
end
使用する時には下記のように記述します。
1
validates :カラム名, バリデーション名: true
上の例だとバリデーション名をNameCheck
としているのでname_check
という形で使用します。
1
validates :name, name_check: true
この時カラム名は下記のように複数指定することもできます。
1
validates :name, :age, name_check: true
この時、実際どのようなことが行われている確認をしてみましょう。
1
2
3
4
5
6
record=> #<User:0x00007ffcd811eef0
id: nil,
name: 'pikawaka',
age: 27,
created_at: nil,
updated_at: nil>
上のようなレコードがあったとします。
カスタムバリデーションはvalidate_each(record, attribute, value)
メソッドで作成しました。
3つの引数は順番に(保存する前のテーブルのレコード, カラム名, 送信した値)が入るのでした。
上の例の場合def validate_each(record, attribute, value)
のrecord、attribute、valueには下記の内容が入ります。
record
には上のレコードの内容が入ります。
attribute
にはカラム名が入るのでここでは最初に指定した「name」が入ります。
value
には送信した値である「pikawaka」が入ります。
そして今回はカラムを2つ指定してるので、eachのように繰り返しになってます。
次は:age
の番です。
またvalidate_each
のメソッドが実行され下記の内容が引数にセットされます。
record
には1番目と同様に上のレコードの内容が入ります。
attribute
にはカラム名が入るのでここでは「age」が入ります。
value
には送信した値である「27」が入ります。
このような流れで実行されます。
validateメソッドを使った方法
他にもvalidateメソッド
を使用してバリデーションを作成することもできます。
validates
のようにs
が付かないので気をつけましょう。
1
2
3
4
5
validate :メソッド名
def メソッド名
検証したいコード
end
実際は下記のように定義します。
1
2
3
4
5
6
7
validate :check_name
def check_name
if name == "管理人"
errors.add(:name, "その名前は使用できません")
end
end
一時的なバリデーションのスキップ方法
一時的に検証を行わないようにするには下記のように記述します。
1
メソッド(validate: false)
下記のように使います。
1
2
3
4
5
6
7
article = Arrticle.new(title: "")
article.save(validate: false)
# 検証がスキップされるので保存が実行される
(1.1ms) BEGIN
Arrticle Create (54.9ms) INSERT INTO `articles` (`title`, `created_at`, `updated_at`) VALUES ('', '20XX-XX-XX XX:XX:XX', '20XX-XX-XX XX:XX:XX')
(26.7ms) COMMIT
=> true
ただし思わぬバグが発生することがあるので、使う際は十分に注意しましょう。
この記事のまとめ
- バリデーションとはデータベースにデータを保存するときに内容を検証してくれる機能のこと
- ヘルパーを使うことで色々な検証を行うことができる
- valid?メソッドとerrorsメソッドを使えばどの検証に引っかかっているかを確認することができる
- カスタムバリデーションで自分で作成した検証を行うことができる