🔥

【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

この例だと、nameemailに空白が含まれているとエラーになります。

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を設定することで、アプリケーションとデータベースの両方で不整合を防ぐことができます。

参考

https://railsguides.jp/active_record_validations.html

Discussion