Edited at

Railsバリデーションまとめ

More than 1 year has passed since last update.

オブジェクトがDBに保存される前に、そのデータが正しいかどうかを検証する仕組みをバリデーションといいますが、

RailsでActiveRecordを使ってそれを実現するにあたってよく使いそうなのをまとめます。

以下のメソッドにおいてはバリデーションがトリガされます。


  • create

  • create!

  • save

  • save!

  • update

  • update!

以下のメソッドにおいてはバリデーションはスキップされます。


  • decrement!

  • decrement_counter

  • increment!

  • increment_counter

  • toggle!

  • touch

  • update_all

  • update_attribute

  • update_column

  • update_columns

  • update_counters


Railsでのバリデーショントリガ

Railsではvalid?メソッドを実行するとバリデーションが実行されます。

バリデーションが通ればtrueを返し、引っかかればfalseを返します。

ちなみにinvalid?メソッドは逆の振る舞いをします。


バリデーションヘルパー

ActiveRecordには多くのバリデーションヘルパーが準備されています。

以下のオプションは全てのヘルパーで使用することが出来ます。

オプション
概要

:allow_nil
対象の値がnilの場合にバリデーションをスキップします。

:allow_blank
対象の値がblank? => trueの場合にバリデーションをスキップします。

:on
バリデーションの実行のタイミングを設定できます。:createや:updateを指定するとその場合にのみバリデーションが行われます。

:message
エラーメッセージを設定することが出来ます。もし、このオプションがない場合にはデフォルトのメッセージが表示されます。

以降、用途に応じてバリデーションヘルパーの使用例を掲載します。


存在


空でないこと


model

validates :title, presence: true



booleanの場合


model

validates :completed, inclusion: { in: [true, false] }



チェックボックス


model

validates :category, acceptance: true



空であること


model

validates :login, absence: true



一意性(ユニーク)


model

validates :user_name, uniqueness: true



一致

指定した属性名#{指定した属性名}_confirmationを比較します。


model

validates :email, confirmation: true



含む、含まない


inclusion

含むかどうかを検証する場合


model

validates :kind, inclusion: { in: %w(draft publish private) }



exclusion

含まないことを検証する場合


model

validates :subdomain, exclusion: { in: %w(www us ca jp) }



長さ


model

validates :title,    length: { minimum: 1 }       # 「1文字以上」

validates :title, length: { maximum: 75 } # 「75文字以下」
validates :title, length: { in: 1..75 } # 「1文字以上75文字以下」
validates :password, length: { is: 8 } # 「8文字のみ」


フォーマット


数値


model

validates :age, numericality: true


またnumericalityには便利なオプションが多数用意されています。

オプション
概要

:only_integer
integerのみ

:greater_than
指定された値よりも大きいか

:greater_than_or_equal_to
指定された値と等しい、あるいは大きいか

:equal_to
指定された値と等しいか

:less_than
指定された値よりも小さいか

:less_than_or_equal_to
指定された値と等しいか、あるいは小さいか

:odd
trueに設定した場合、奇数か

:even
trueに設定した場合、偶数か


メールアドレス


model

VALID_EMAIL_REGEX = /\A[\w+\-.]+@[a-z\d\-.]+\.[a-z]+\z/i

validates :email, presence: true, uniqueness: true, format: { with: VALID_EMAIL_REGEX }


関連

has_manyなどでモデルが関連付けられていて、両方のモデルに対してバリデーションを実行するときに使います。


model

validates_associated :books



validates_associatedは関連付けの両側のオブジェクトでは実行しないでください。

関連付けの両側でこのヘルパーを使用すると無限ループになります。


だそうなのでご注意ください。


条件付きのバリデーション

特定の条件の場合にのみバリデーションを有効にしたい場合などがあります。その場合は、:ifや:unlessを使用して条件を設定することができます。


model

validates :card_number, presence: true, if: :paid_with_card?

def paid_with_card?
payment_type == "card"
end


以下のようにProcを使用して書くこともできます。


model

validates :password, confirmation: true,

unless: Proc.new { |a| a.password.blank? }

条件を複数指定する場合は配列にすることで実現できます。


model

if: [:is_admin?, "password.blank?"]



カスタムバリデーション

バリデーションヘルパーで実現出来ない場合には自分でメソッドやクラスを作成することができます。


カスタムメソッド

validateには , 区切りで複数のメソッドを指定することができます。


model

validate :date_cannot_be_in_the_past

def date_cannot_be_in_the_past
if date.present? && date < Date.today
errors.add(:date, ": 過去の日付は使用できません")
end
end



カスタムバリデータ

カスタムバリデータはActiveModel::Validatorを拡張したクラスです。作成したクラスではvalidateメソッドが実装されている必要があり、このメソッドはレコードを1つ引数に取り、それに対してバリデーションを実行します。カスタムバリデータはvalidates_withメソッドを使用して呼び出します。


model

class TelephoneValidator < ActiveModel::Validator

def validate(record)
unless record.tel.starts_with? '0'
record.errors[:name] << '電話番号は0から始まる必要があります'
end
end
end

class User
include ActiveModel::Validations
validates_with TelephoneValidator
end


ActiveModel:: EachValidatorを拡張すると更に便利に個別の属性を検証することができます。この場合validate_eachメソッドを実装する必要があります。このメソッドは、そのインスタンスに対応する「レコードと属性と値」、バリデーションを行なう属性、そして渡されたインスタンスの属性の値の3つの引数を取ります。


model

class ImageFileTypeValidator < ActiveModel::EachValidator

def validate_each(record, attribute, value)
if value.present?
file_type = File::extname(value.to_s)
file_types = %w(.jpg .jpeg .png .gif)
record.errors[attribute] << I18n.t('errors.messages.invalid_image') unless file_types.include?(file_type)
end
end
end

class Image < ActiveRecord::Base
validates :img, presence: true, image_file_type: true
end



参考

Rails4でモデルにバリデーションを実装する - Rails Webook

http://ruby-rails.hatenadiary.com/entry/20140724/1406145303

Active Record バリデーション — Rails ガイド

http://railsguides.jp/active_record_validations.html