Part1 続き
前回の記事では、Railsのバリデーションのおおまかな概要についてまとめました📘
今回は具体的なバリデータについてまとめていきたいと思います!
バリデーションの種類
ここからは実際にコードにどのようにバリデーションを記述するかまとめていきます!
先に今回紹介するバリデーションを一覧しました✍️
| バリデーション名 | 説明 |
|---|---|
presence |
属性が空でないことを検証 |
absence |
属性が空であることを検証 |
uniqueness |
属性の値がユニークであることを検証 |
inclusion |
属性の値が特定のセットに含まれていることを検証 |
exclusion |
属性の値が特定のセットに含まれていないことを検証 |
acceptance |
利用規約などのチェックボックスにチェックが入っていることを検証 |
バリデーションの定義方法
ActiveRecordでは自分でバリデーションのルールを書かずとも、すでに多数のバリデーションが用意されているので、まずそれらについて確認していきます!
ActiveModelにて提供されているvalidatesメソッドを使用して、モデルクラス内でバリデーションを定義します。
validates :属性名, バリデーションの種類: オプション
presence
指定した属性が「空でない」ことを検証するバリデーションです。最もよく使われるバリデーションの1つです⭐️
class Book < ApplicationRecord
validates :title, presence: true
end
この場合titleが空であればエラーになり、エラーメッセージは「Title can't be blank」となります。
関連付けが存在することを確認したい場合、外部キーが存在するのかどうかを検証するのではなく、関連付けられたオブジェクト自体が存在するかどうかを検証します。
class Book < ApplicationRecord
# ❌ 間違い: author_idが存在するか検証
validates :author_id, presence: true
# ⭕ 正しい: authorオブジェクトが存在するか検証
belongs_to :author
validates :author, presence: true
end
またboolean型の属性に対して、存在を検証する場合は注意が必要です!
同じようにpresence: trueを使用すると、falseの場合にエラーとなってしまいます😥
falseも有効な値として扱いため、inclusionまたはexclusionを使用して検証する必要があります。
Class Book < ApplicationRecord
# ❌ 間違い: falseの場合にエラーになる
validates :published, presence: true
# ⭕ 正しい: trueまたはfalseを許可
validates :published, inclusion: [true, false]
# または
validates :published, exclusion: [nil]
end
空でないことをどうやってチェックしているの?
presenceバリデータの内部では、Object#blank?メソッドを使用して、属性の値が空であるかどうかを判断しています。
具体的には、以下の条件で空とみなされます。
-
nilである - 空の文字列(
"") - スペース、タブ、改行の空白のみの文字列(
" ") - 空の配列(
[]) - 空のハッシュ(
{})
absence
presenceバリデータの逆の動作をします。指定した属性が「空である」ことを検証します。また検証内容の性質上、ifオプションと組み合わせた条件付きバリデーションで使用される場面が多いです。
class Book < ApplicationRecord
validates :coupon_code, absence: true, if: :gest_user?
end
この場合、gest_user?メソッドがtrueを返す場合にのみ、coupon_codeが空であることを検証します。エラーメッセージは「Coupon code must be blank」となります。
空であることのチェックは、Object#present?メソッドを使用して行われます!
uniqueness
属性の値が一意(ユニーク)であり他のレコードと重複していないことを検証します。メールアドレスやユーザー名など、重複を許さない属性に対してよく使用されます。
class User < ApplicationRecord
validates :email, uniqueness: true
end
この場合、emailが他のレコードと重複している場合にエラーとなり、エラーメッセージは「Email has already been taken」となります。
データベースレベルのユニーク制約も必要
-
uniquenessバリデーションはアプリケーションレベルでの検証であり、同時に複数のリクエストが来た場合などに重複レコードが作成される可能性があります😱 - この現象をレースコンディションと言うらしいです💦
- そのため、データベースレベルでもユニーク制約を設定することがベストプラクティスとされています!
scopeオプション
特定のカラムの組み合わせで一意性を検証したい場合は、scopeオプションを使用します。
class Employee < ApplicationRecord
validates :email, uniqueness: { scope: :organization_id }
end
この場合、同じorganization_id(組織)内でemailが一意であることを検証します。
複数カラムの組み合わせで一意性を検証したい場合は、scopeオプションに配列を渡します。
class Employee < ApplicationRecord
validates :email, uniqueness: { scope: [:organization_id, :department_id] }
end
この場合、同じorganization_id(組織)かつdepartment_id(部署)内でemailが一意であることを検証します。
case_sensitiveオプション
デフォルトではuniquenessバリデーションは大文字と小文字を区別しますが、case_sensitive: falseオプションを指定することで、大文字と小文字を区別せずに検証することができます。
class User < ApplicationRecord
validates :username, uniqueness: { case_sensitive: false }
end
conditionsオプション
特定の条件に基づいて一意性を検証したい場合は、conditionsオプションを使用します。
SQLのWHERE句に相当する条件を指定できます。
class Article < ApplicationRecord
validates :title, uniqueness: { conditions: -> { where(status: 'published') }
}
end
この場合、statusがpublishedのレコードに対してのみtitleの一意性を検証します。
inclusionとexclusion
これらのバリデータは、属性の値が特定のセットに「含まれているか」「含まれていないか」をチェックします。
指定するセットには、任意のenumerableオブジェクト(配列やrange、procやlambdaやシンボルで動的に生成されたコレクションなど)を利用できます。
参考:Railsガイド バリデーションの使い方より
このように、自分でセットを指定して、その中に含まれているか確認したいときはinclusion、含まれていないかを検証したいときはexclusionを使用します。
class User < ApplicationRecord
validates :age, inclusion: { in: 18..65 }
validates :username, exclusion: { in: %w[admin root superuser] }
end
この場合、ageが18から65の範囲内であることを検証し、usernameがadmin、root、superuserのいずれかでないことを検証します。
inオプションで値のセットが渡せれるようになっています。
acceptance
ユーザーがチェックボックスにチェック✅を入れたかどうかを検証するバリデーションです。
利用規約への同意やプライバシーポリシーの確認など、ユーザーの明示的な同意が必要な場面で使用します。
class User < ApplicationRecord
validates :terms_of_service, acceptance: true
end
この場合、terms_of_service属性がtrueであることを検証します。エラーメッセージは「Terms of service must be accepted」となります。
このバリデータの特徴としては、データベースにカラムが存在しなくても動作する点です。
具体的にはフォームから送信された値だけを検証し、データベースには保存されません。
acceptanceオプション
acceptanceオプションを使用して、受け入れ可能な値をカスタマイズできます。デフォルトでは[1, true]が受け入れ可能な値ですが、他の値も指定できます。
class User < ApplicationRecord
validates :terms_of_service, acceptance: { accept: ['yes', 'YES', 'Y'] }
end
この場合、terms_of_service属性が'yes'、'YES'、または'Y'であることを検証します。
Part3へ続く
参考記事
