オブジェクトが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 | エラーメッセージを設定することが出来ます。もし、このオプションがない場合にはデフォルトのメッセージが表示されます。 |
以降、用途に応じてバリデーションヘルパーの使用例を掲載します。
存在
空でないこと
validates :title, presence: true
booleanの場合
validates :completed, inclusion: { in: [true, false] }
チェックボックス
validates :category, acceptance: true
空であること
validates :login, absence: true
一意性(ユニーク)
validates :user_name, uniqueness: true
一致
指定した属性名
と#{指定した属性名}_confirmation
を比較します。
validates :email, confirmation: true
含む、含まない
inclusion
含むかどうかを検証する場合
validates :kind, inclusion: { in: %w(draft publish private) }
exclusion
含まないことを検証する場合
validates :subdomain, exclusion: { in: %w(www us ca jp) }
長さ
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文字のみ」
フォーマット
数値
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に設定した場合、偶数か |
メールアドレス
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などでモデルが関連付けられていて、両方のモデルに対してバリデーションを実行するときに使います。
validates_associated :books
validates_associatedは関連付けの両側のオブジェクトでは実行しないでください。
関連付けの両側でこのヘルパーを使用すると無限ループになります。
だそうなのでご注意ください。
条件付きのバリデーション
特定の条件の場合にのみバリデーションを有効にしたい場合などがあります。その場合は、:ifや:unlessを使用して条件を設定することができます。
validates :card_number, presence: true, if: :paid_with_card?
def paid_with_card?
payment_type == "card"
end
以下のようにProcを使用して書くこともできます。
validates :password, confirmation: true,
unless: Proc.new { |a| a.password.blank? }
条件を複数指定する場合は配列にすることで実現できます。
if: [:is_admin?, "password.blank?"]
カスタムバリデーション
バリデーションヘルパーで実現出来ない場合には自分でメソッドやクラスを作成することができます。
カスタムメソッド
validateには ,
区切りで複数のメソッドを指定することができます。
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メソッド
を使用して呼び出します。
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つの引数を取ります。
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