【Rails】Active Record バリデーションの使い方
バリデーションとは
バリデーションは、モデルに保存されるデータが正しいかどうかを確認する仕組みです。
たとえばユーザー登録時に名前が空だったり、メールアドレスが不正な形式だった場合に保存を防ぐことができます。
バリデーションはモデルに記述します。コントローラやビューではなくモデルにまとめることで、データの一貫性を保つことができます。
バリデーションの種類
presence
値が空でないことを確認します。
class User < ApplicationRecord
validates :name, presence: true
end
この例ではnameが空のままだとエラーになります。
absence
値が空であることを確認します。特定の属性を必ず空にしたい場合に使います。
class User < ApplicationRecord
validates :admin, absence: true
end
この例ではadminに値が入っているとエラーになります。
length
文字列の長さを制限します。
主なオプション:
-
minimum: 最小文字数 -
maximum: 最大文字数 -
is: ちょうどの文字数 -
within/in: 範囲指定
class User < ApplicationRecord
validates :name, length: { minimum: 3, maximum: 20 }
end
この例だと名前は3文字以上20文字以内でなければエラーになります。
numericality
数値であることを確認します。整数のみを許可したり、範囲を指定することも出来ます。
主なオプション:
-
only_integer: 整数のみ許可 -
greater_than,less_than_or_equal_toなど
class Product < ApplicationRecord
validates :price, numericality: { only_integer: true, greater_than: 0 }
end
この例だと価格が整数で0より大きくなければ保存できません。
uniqueness
値が一意であることを確認します。
class User < ApplicationRecord
validates :email, uniqueness: true
end
この例だと同じメールアドレスの登録はできません。
ただしuniquenessはアプリケーション側でのチェックに過ぎないため、必ずデータベース側にもユニーク制約を付与する必要があります。
format
正規表現で値の形式をチェックします。
主なオプション:
-
without: 正規表現にマッチしない場合のみ有効
class User < ApplicationRecord
validates :password,
format: {
with: /\A(?=.*[a-z])(?=.*[A-Z])(?=.*\d).*\z/,
message: "は小文字、大文字、数字をそれぞれ1文字以上含む必要があります"
},
if: :password_required?
private
def password_required?
!persisted? || !password.nil? || !password_confirmation.nil?
end
end
この例では以下のように動作します。
-
Abc123→ 🙆♂️(小文字・大文字・数字をすべて含む) -
abcdef→ 🙅♂️(大文字・数字が含まれていない) -
ABCDEF→ 🙅♂️(小文字・数字が含まれていない) -
123456→ 🙅♂️(小文字・大文字が含まれていない)
また、:ifをつかうことで特定の条件下でのみバリデーションが動くようになります。
inclusion、exclusion
値が特定の集合に含まれる(または含まれない)ことを確認します。
主なオプション:
-
in: 受け付けるor受け付けない値の集合を指定
class Coffee < ApplicationRecord
validates :size, inclusion: { in: %w(small medium large),
message: "%{value} のサイズは無効です" }
end
この例だとsizeが指定の配列に含まれていないとエラーになります。
逆にexclusionを使うとその集合に含まれていた時にエラーになります
confirmation
確認用の入力と一致していることを確認します。パスワードなどでよく使われます。
class User < ApplicationRecord
validates :password, confirmation: true
end
この場合、password_confirmationという属性と一致しなければエラーになります。
acceptance
フォームなどのチェックボックスがオンになっているかどうかを確認します。
利用規約への同意確認などに使われます。
class User < ApplicationRecord
validates :terms_of_service, acceptance: true
end
値がtrue(チェックあり)でなければエラーになります。
comparison
Rails7で追加された比較用のバリデーションです。数値や日付を簡単に比較できます。
class Event < ApplicationRecord
validates :end_date, comparison: { greater_than: :start_date }
end
この例だと終了日が開始日より後でなければエラーになります。
validates_associated
関連する子モデルも正しいかどうかを確認します。
class User < ApplicationRecord
has_many :posts
validates_associated :posts
end
この例では、ユーザー保存時に関連するpostsが不正だとエラーになります。
validates_each
複数の属性に対して細かいカスタムチェックを行うことができます。
class User < ApplicationRecord
validates_each :name, :email do |record, attr, value|
record.errors.add(attr, "に空白を含めることはできません") if value&.include?(" ")
end
end
この例だと、nameとemailに空白が含まれているとエラーになります。
validates_with
専用のバリデーションクラスを定義します
class EmailValidator < ActiveModel::Validator
def validate(record)
unless record.email&.end_with?("@example.com")
record.errors.add(:email, "は@example.comで終わる必要があります")
end
end
end
class User < ApplicationRecord
validates_with EmailValidator
end
専用のクラスにまとめることで、複雑な条件でも読みやすく再利用可能になります。
まとめ
バリデーションはRailsのコード上で不正データを防ぐための仕組みですが同時にデータベースの制約も設定しておくことが大事です。
例えばuniquenessだけに頼ると同時に複数のリクエストが走った場合に重複データが保存されてしまう可能性があります。
そのためRailsのvalidatesに加えてデータベース側にもNOT NULL制約やUNIQUEを設定することで、アプリケーションとデータベースの両方で不整合を防ぐことができます。
参考
Discussion