Activeレコードの検証

このガイドでは、Active Recordの検証機能を使用してデータベースに入れる前に、 オブジェクトの状態を検証する方法について説明していきます。

このガイドを読むことで、次の事が学べるはずです。

  • 組み込みのActive Record検証ヘルパーの使い方
  • 開発者定義の検証メソッドの作成方法
  • 検証プロセスにより、どのようにメッセージが生成されるか

1. 検証の概要

ここで、非常にシンプルな検証の例を示します。

class Person < ActiveRecord::Base
  validates :name, presence: true
end

Person.create(name: "John Doe").valid? # => true
Person.create(name: nil).valid? # => false

ご覧の通り、検証はname属性の無いPersonが不正であることを知らせてくれます。 この例の2つ目のPersonは、データベースに保存されません。

更に詳細について学ぶ前に、検証をアプリケーションに適用させることについての全体像について話をしましょう。

1.1 なぜ、検証するのか?

2. 検証ヘルパー

Active Recordは、開発者が定義したクラス内で直接使用出来る多くの定義済み検証ヘルパーを提供してくれます。 これらのヘルパーは共通の検証ルールを提供します。 検証が失敗した際は、エラーメッセージがオブジェクトのerrorsコレクションに追加され、 このメッセージは検証された属性と関連付けられています。

各ヘルパーには、属性名を好きなだけ指定することができ、 1行で同じ種類の検証を複数の属性に追加することが可能です。

全ての検証ヘルパーは、実行されるべきタイミングを決める:onと、 検証が失敗した際にerrorsコレクションに追加すべきメッセージを決める:messageオプションを受け入れます。 :onオプションは、:save(デフォルト)、:create:updateのうちの1つを指定することが出来ます。 検証ヘルパーのそれぞれに対して、デフォルトのエラーメッセージがあり、これらのメッセージは:messageオプションが指定されなかった際に使用されます。 それでは、利用可能な各検証ヘルパーについて見て行きましょう。

2.1 acceptance

このメソッドは、フォームが送信された際にユーザーがチェックボックスをチェックしたかを検証します。 これは、アプリケーションのサービス利用規約、文章を読んでいるのかの確認、その他同種のことで、ユーザーに同意を求める際に使用されます。 この検証は非常にWebアプリケーション固有の性質を持ち、また「同意」のデータはデータベースに保存する必要はありません。 (このカラムフィールドが無くても、ヘルパーは仮想的に属性を作成してくれます。)

class Person < ActiveRecord::Base
  validates :terms_of_service, acceptance: true
end

デフォルトのエラーメッセージは、"must be accepted"です。

また、:acceptオプションの受け取りが可能で、同意した際に検証する値を決めることが出来ます。 デフォルトは、"1"で、他のものにしたければ下記のようにして簡単に変更することが出来ます。

class Person < ActiveRecord::Base
  validates :terms_of_service, acceptance: { accept: 'yes' }
end

2.2 validates_associated

このヘルパーは、モデルが他のモデルに関連付けられていて検証が必要な場合に使用すべきです。 オブジェクトを保存しようとした際に、関連付けられたオブジェクトそれぞれでvalid?が呼び出されます。

class Library < ActiveRecord::Base
  has_many :books
  validates_associated :books
end

この検証は、全ての関連付けのタイプで動作します。

validates_associatedを関連付けの両方で使用しないでください。 相互に呼び出しを行い、無限ループが発生してしまいます。

validates_associatedのデフォルトのエラーメッセージは、"is invalid"です。 関連付けられた各オブジェクトは、自身のerrorsコレクションを持ち、 errorsは呼び出し元のモデルを、把握しない(翻訳に自信無し)ことに注意してください。

2.3 confirmation

このヘルパーは、2つの入力欄に同じ値が入力されているかを検証する際に使用されます。 例えば、メールアドレスやパスワードを確認したい場合などです。 この検証は、確認用に"_confirmation"が付加されたフィールド名の仮想の属性を作成します。

class Person < ActiveRecord::Base
  validates :email, confirmation: true
end

ビューは、次のようにします。

<%= text_field :person, :email %>
<%= text_field :person, :email_confirmation %>

このチェックは、email_confirmationがnilで無かった場合にのみ動作します。 必須の確認を行うには、presenceチェックを追加してください。 (後述のpresenceを確認してください。)

class Person < ActiveRecord::Base
  validates :email, confirmation: true
  validates :email_confirmation, presence: true
end

デフォルトのエラーメッセージは、"doesn't match confirmation"です。

2.4 exclusion

このヘルパーは属性の値が、与えられた集合に含まれていないを検証します。 この集合には、任意の列挙可能なオブジェクトを指定することが可能です。

class Account < ActiveRecord::Base
  validates :subdomain, exclusion: { in: %w(www us ca jp),
    message: "Subdomain %{value} is reserved." }
end

exclusionヘルパーは、:inオプションを持ち、 これに指定された各値の場合、検証で拒否されます。 :inは、:withinのエイリアスを持ち、同じ働きをします。 この例では、:messageオプションを使って、拒否された理由を表示するようにしています。

デフォルトのエラーメッセージは、"is reserved"です。

2.5 format

このヘルパーは、属性の値が:withオプションに指定した正規表現にマッチするかどうかを検証します。

class Product < ActiveRecord::Base
  validates :legacy_code, format: { with: /A[a-zA-Z]+z/,
    message: "Only letters allowed" }
end

デフォルトのエラーメッセージは、"is invalid"です。

2.6 inclusion

このヘルパーは値が指定された集合に含まれているかどうかを検証します。 この集合には、任意の列挙可能なオブジェクトを指定することが可能です。

class Coffee < ActiveRecord::Base
  validates :size, inclusion: { in: %w(small medium large),
    message: "%{value} is not a valid size" }
end

inclusionヘルパーは、:inオプションを持ち、 これに指定された各値の場合のみ、検証で受け入れられます。 :inは、:withinのエイリアスを持ち、同じ働きをします。 この例では、:messageオプションを使って、受け入れられない理由を表示するようにしています。

デフォルトのエラーメッセージは、"is not included in the list"です。

2.7 length

このヘルパーは、値の長さを検証します。 このヘルパーは様々なオプションを持つため、様々な方法で長さの制約を指定することが出来ます。

class Person < ActiveRecord::Base
  validates :name, length: { minimum: 2 }
  validates :bio, length: { maximum: 500 }
  validates :password, length: { in: 6..20 }
  validates :registration_number, length: { is: 6 }
end

lengthの利用可能なオプションは下記のとおりです。

:minimum
指定された長さよりも、小さくすることはできません。
:maximum
指定された長さよりも、大きくすることはできません。
:inor (:within)
属性の長さは、与えられた区間に含まれている必要があり、 このオプションの値の範囲でなければなりません。
:is
長さは与えられた値に等しくなければなりません。

デフォルトのエラーメッセージは、検証内容によって異なります。 これらのメッセージを:wrong_length:too_long:too_shortオプションと、 %{count}を制約としている数値のプレースホルダを使用することで、独自のメッセージを作成することが可能です。 また、:messageをここでも使用することが可能です。

class Person < ActiveRecord::Base
  validates :bio, length: { maximum: 1000,
    too_long: "%{count} characters is the maximum allowed" }
end

このヘルパーはデフォルトで文字数をカウントしますが、 :tokenizerオプションを使用して、異なる方法に値を分けることが可能です。

class Essay < ActiveRecord::Base
  validates :content, length: {
    minimum: 300,
    maximum: 400,
    tokenizer: lambda { |str| str.scan(/w+/) },
    too_short: "must have at least %{count} words",
    too_long: "must have at most %{count} words"
  }
end

デフォルトのエラーメッセージが複数あることに注意してください(例: "is too short (minimum is %{count} characters)")。 このようになっている理由は、:minimumが1の場合、独自のメッセージを提供するか、 代わりにpresence: trueを使用すべきだからです。 :inまたは:withinが1の最小限の指定がされている場合、 独自のメッセージを提供するか、優先的にpresenceを使用すべきです。

sizeヘルパーは、lengthへのエイリアスです。

2.8 numericality

このヘルパーは、属性が数値だけなのかを検証します。 デフォルトでは、任意の整数、浮動小数点数を許可します。 整数のみを許可したい場合は、:only_integerにtrueを指定します。

/A[+-]?d+Z/

正規表現は、値の属性を検証します。 そうでなければ、値をFloatを使用して数値にしようと試みます。

上記の正規表現は末尾に改行文字を許可することに注意してください。

class Player < ActiveRecord::Base
  validates :points, numericality: true
  validates :games_played, numericality: { only_integer: true }
end

また、:only_integerは下記のオプションを受け入れる値の制約に付け加える事が可能です。

:greater_than
指定された値より、大きな値でなければいけません。 このオプションのデフォルトのエラーメッセージは、"must be greater than %{count}"です。
:greater_than_or_equal_to
指定された値より等しいか、それより大きな値でなければいけません。 このオプションのデフォルトのエラーメッセージは、"must be greater than or equal to %{count}"です。
:equal_to
指定された値と、等しい値でなければいけません。 このオプションのデフォルトのエラーメッセージは、"must be equal to %{count}"です。
:less_than
指定された値より、小さな値でなければいけません。 このオプションのデフォルトのエラーメッセージは、"must be less than %{count}"です。
:less_than_or_equal_to
指定された値と等しいか、それより小さな値でなければいけません。 このオプションのデフォルトのエラーメッセージは、"must be less than or equal to %{count}"です。
:odd
tureがセットされた場合、奇数でなければいけません。 このオプションのデフォルトのエラーメッセージは、"must be odd"です。
:even
tureがセットされた場合、偶数でなければいけません。 このオプションのデフォルトのエラーメッセージは、"must be even"です。

デフォルトのエラーメッセージは、"is not a number"です。

2.9 presence

このヘルパーは、属性が空では無いことを検証します。 これにはblank?メソッドが使用され、nilか空白の文字列、どちらかでないかを調べます。 空白の文字列とは、空文字列、空白スペースのみで構成された文字列が対象となります。

class Person < ActiveRecord::Base
  validates :name, :login, :email, presence: true
end

関連付けされたものを必須項目にしたい場合は、 関連付けられているオブジェクト自体が存在し、関連付けに外部キーが使用されていないか(翻訳に自信無し)を確認する必要があります。

class LineItem < ActiveRecord::Base
  belongs_to :order
  validates :order, presence: true
end

関連付けされたレコードを必須とするには、 :inverse_ofオプションを関連付けに指定する必要があります。

class Order < ActiveRecord::Base
  has_many :line_items, inverse_of: :order
end

has_onehas_manyを通して関連付けられたオブジェクトの必須検証をする場合、 オブジェクトがblank?marked_for_destruction?のどちらでも無いことを調べます。

false.blank?はtrueであるため、booleanフィールドの必須検証をしたい場合は、 :field_name inclusion: { in: [true, false] }を使用すべきです。

デフォルトのエラーメッセージは、"can't be empty"です。

2.10 absence

このヘルパーは、指定された属性の値が空であることを検証します。 これにはpresent?メソッドが使用され、nilか空白の文字列、どちらでも無いかを調べます。 空白の文字列とは、空文字列、空白スペースのみで構成された文字列が対象となります。

class Person < ActiveRecord::Base
  validates :name, :login, :email, absence: true
end

関連付けされたものが存在しないようにしたい場合は、 関連付けられているオブジェクト自体が存在せず、関連付けに外部キーが使用されていないか(翻訳に自信無し)を確認する必要があります。

class LineItem < ActiveRecord::Base
  belongs_to :order
  validates :order, absence: true
end

関連付けされたレコードが存在しないことを必須とするには、 :inverse_ofオプションを関連付けに指定する必要があります。

class Order < ActiveRecord::Base
  has_many :line_items, inverse_of: :order
end

has_onehas_manyを通して関連付けられたオブジェクトが存在しないことを検証をする場合、 オブジェクトがpresent?marked_for_destruction?のどちらでも無いことを調べます。

false.present?はfalseであるため、booleanフィールドの必須検証をしたい場合は、 :field_name exclusion: { in: [true, false] }を使用すべきです。

デフォルトのエラーメッセージは、"must be blank"です。

2.11 uniqueness

このヘルパーは、属性の値がオブジェクトが保存される直前に一意であることを検証します。 ただし、データベースの一意制約を作るわけではないので、異なるデータベース接続によって 一意指定されているカラムに対し、同じ値が入ってしまうことがあるかもしれません。 これを避けるために、データベースにユニークインデックスを作成するようにしてください。

class Account < ActiveRecord::Base
  validates :email, uniqueness: true
end

この検証では、モデルのテーブルに対しSQLクエリーを発行し、 同じ値のレコードが存在しないかを検索します。

他の属性に指定することで、一意制約の範囲の指定を行うの事ができる:scopeオプションがあります。

class Holiday < ActiveRecord::Base
  validates :name, uniqueness: { scope: :year,
    message: "should happen once per year" }
end

また、一意制約の際に大文字と小文字を区別するか、しないかを決定する:case_sensitiveオプションもあります。 このオプションのデフォルトはtrueです。

class Person < ActiveRecord::Base
  validates :name, uniqueness: { case_sensitive: false }
end

いずれにしても大文字・小文字を区別した検索が行われるように設計されたデータベースがある事に注意してください。

デフォルトのエラーメッセージは、"has already been taken"です。

2.12 validates_with

このヘルパーは、検証のために別のクラスにレコードを渡します。

class Person < ActiveRecord::Base
  validates_with GoodnessValidator
end

class GoodnessValidator < ActiveModel::Validator
  def validate(record)
    if record.first_name == "Evil"
      record.errors[:base] << "This person is evil"
    end
  end
end

record.errors[:base]は、特定の属性では無く、全体の状態に関するエラーを追加します。

validates_withヘルパーには、検証に使用するクラス、または複数のクラスのリストを指定します。 validates_withには、デフォルトのエラーメッセージが無いため、 検証クラス内で、レコードのエラーコレクションに手動でエラーを追加しなければいけません。

検証メソッドの実装には、パラメーターが定義されているレコードを持たなければいけません。

他の全ての検証のように、validates_with:if:unless:onオプションを指定することが出来ます。 もし、他のオプションを渡すと、それらのオプションは検証クラスのオプションとして渡されます。

class Person < ActiveRecord::Base
  validates_with GoodnessValidator, fields: [:first_name, :last_name]
end

class GoodnessValidator < ActiveModel::Validator
  def validate(record)
    if options[:fields].any?{|field| record.send(field) == "Evil" }
      record.errors[:base] << "This person is evil"
    end
  end
end

この検証は、検証実行毎にでは無く、アプリケーションのライフサイクル全体で1度だけしか初期化されないことに注意してください。 そのため、内部のインスタンス変数の使用について注意が必要です。

インスタンス変数を使用した検証が複雑になってしまう場合は、 簡単にするために、代わりにプレーンな古いRubyオブジェクトを使用することも可能です。

class Person < ActiveRecord::Base
  validate do |person|
    GoodnessValidator.new(person).validate
  end
end

class GoodnessValidator
  def initialize(person)
    @person = person
  end

  def validate
    if some_complex_condition_involving_ivars_and_private_methods?
      @person.errors[:base] << "This person is evil"
    end
  end

  # …
end

2.13 validates_each

このヘルパーは、属性の検証をブロックを通して行います。 検証関数は予め定義されていないため、使用するブロックを1つ作成し、 渡された各属性毎に調べるようにしなければいけません。 下記の例では、nameとsurnameが小文字から始まっていないことを検証しています。

class Person < ActiveRecord::Base
  validates_each :name, :surname do |record, attr, value|
    record.errors.add(attr, 'must start with upper case') if value =~ /A[a-z]/
  end
end

ブロックはレコード、属性名、属性の値を受け取ります。 ブロック内で、正当なデータであるかどうかを好きなように検証することが可能です。 もし、検証を失敗とするのであれば、不当なデータであるとするために、モデルにエラーメッセージを追加してください。

3. 共通の検証オプション

共通の検証オプションです。

3.1 :allow_nil

:allow_nilオプションは、検証する値がnilの場合、検証をスキップします。

class Coffee < ActiveRecord::Base
  validates :size, inclusion: { in: %w(small medium large),
    message: "%{value} is not a valid size" }, allow_nil: true
end

3.2 :allow_blank

:allow_blankオプションは、:allow_nilに似ています。 属性の値がnilまたは空文字列のようにblank?に該当する場合は、検証をパスさせます。

class Topic < ActiveRecord::Base
  validates :title, length: { is: 5 }, allow_blank: true
end

Topic.create("title" => "").valid?  # => true
Topic.create("title" => nil).valid? # => true

3.3 :message

これまで見てきたとおり、:messageオプションに指定したメッセージが、検証が失敗した際にerrorsコレクションに追加されます。 このオプションを指定しない場合、Active Recordは各検証ヘルパーのデフォルトのメッセージを適用します。

3.4 :on

:onオプションは、検証を実行するタイミングを指定します。 デフォルトでは、全ての組み込み検証ヘルパーは保存時に実行します。(新規作成、更新の両方) on: :createと指定することで新規作成時のみに、また、 on: :updateとすると更新時のみに検証するように変更することが可能です。

class Person < ActiveRecord::Base
  # 更新時であれば、重複したメールが登録可能です。
  validates :email, uniqueness: true, on: :create

  # 新規作成時であれば、数字ではない値が登録可能です。
  validates :age, numericality: true, on: :update

  # デフォルト状態(検証は新規作成、更新時の両方で行われる)
  validates :name, presence: true, on: :save
end

最後の行は、現在見直しているようです。 issueで議論されているように、 Rails 3.2のバージョンで動作しません。

4. 厳密な検証

検証を厳密にするよう指定する事も可能で、これを指定することで、 検証が失敗した際に、ActiveModel::StrictValidationFailedを発生させるようになります。

class Person < ActiveRecord::Base
  validates :name, presence: { strict: true }
end

Person.new.valid?  # => ActiveModel::StrictValidationFailed: Name can't be blank

また、:strictオプションにカスタム例外を指定することが可能です。

class Person < ActiveRecord::Base
  validates :token, presence: true, uniqueness: true, strict: TokenGenerationException
end

Person.new.valid?  # => TokenGenerationException: Token can't be blank

5. 条件付きの検証

時折、ある条件を満たした場合にのみ、検証を行いたいというケースがありますが、 :if:unlessオプションを使用することで、これを行うことができます。 これらのオプションには、シンボル、文字列、Proc、配列のいずれかを指定します。 :ifオプションには、検証を実行したい条件を指定し、 :unlessオプションには、検証を実行しない条件を指定します。

5.1 :if:unlessにシンボルを指定

:if:unlessオプションにシンボルを指定すると、そのシンボル名に一致するメソッドが 検証が実行される直前に呼び出されます。 このオプションで、最も一般的に使用されています。

class Order < ActiveRecord::Base
  validates :card_number, presence: true, if: :paid_with_card?

  def paid_with_card?
    payment_type == "card"
  end
end

5.2 :if:unlessに文字列を指定

文字列を指定すると、evalによって評価されます。そのため、正しいRubyコードにする必要があります。 これを使用するのは、本当に短い条件で指定出来る場合のみに限定すべきです。

class Person < ActiveRecord::Base
  validates :surname, presence: true, if: "name.nil?"
end

5.3 :if:unlessにProcを指定

Procオブジェクトを使用すると、メソッドを分けずにインラインで条件を書く事が可能です。 1行で書くのに非常に適しています。

class Account < ActiveRecord::Base
  validates :password, confirmation: true,
    unless: Proc.new { |a| a.password.blank? }
end

5.4 条件付き検証のグループ化

1つの条件に対し複数の検証を適用したい場合、 with_optionsを使用することで簡単にそれを実現することが可能です。

class User < ActiveRecord::Base
  with_options if: :is_admin? do |admin|
    admin.validates :password, length: { minimum: 10 }
    admin.validates :email, presence: true
  end
end

if: :is_admin?条件を満たすと、with_optionsブロック内の検証が行われます。

5.5 検証条件を連結

複数の条件を定義する際に、検証を実行するかしないかを配列によって指定することが可能です。 更に:if:unlessを同じ検証で適用することが可能です。

class Computer < ActiveRecord::Base
  validates :mouse, presence: true,
                    if: ["market.retail?", :desktop?]
                    unless: Proc.new { |c| c.trackpad.present? }
end

全ての:if内の条件を満たし、且つ:unless内の条件を満たさない場合にのみ、検証は実行されます。

6. カスタム検証の実行

組み込みの検証ヘルパーの機能が要件を十分に満たせない場合、 好きなように自身のValidators、またはvalidate(検証)メソッドを書くことが可能です。

6.1 カスタムValidators

カスタムValidatorsは、ActiveModel::Validatorを拡張したクラスです。 これらのクラスは、引数としてレコードを受け取り、それを検証するvalidateメソッドを実装しなければいけません。 カスタムValidatorは、validates_withメソッドを使用して呼び出されます。

class MyValidator < ActiveModel::Validator
  def validate(record)
    unless record.name.starts_with? 'X'
      record.errors[:name] << 'Need a name starting with X please!'
    end
  end
end

class Person
  include ActiveModel::Validations
  validates_with MyValidator
end

カスタムValidatorsを追加して、個別の属性を検証する簡単な方法は、 便利なActiveModel::EachValidatorを使用することです。 このケースでは、カスタムValidatorクラスは、3つの引数(レコード、属性、値)を受け取る validate_eachメソッドを実装しなければいけません。

class EmailValidator < ActiveModel::EachValidator
  def validate_each(record, attribute, value)
    unless value =~ /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i
      record.errors[attribute] << (options[:message] || "is not an email")
    end
  end
end

class Person < ActiveRecord::Base
  validates :email, presence: true, email: true
end

例で見てきたように、自身のカスタムValidatorと標準の検証を組み合わせることもできます。

6.2 カスタムメソッド

また、モデルの状態を検証して正しくなければ、errorsコレクションにメッセージを追加するメソッドを作成することも可能です。 vaidateクラスメソッドに検証メソッド名をシンボルとして渡して、メソッドを登録しなければいけません。

各クラスのメソッドに複数のシンボルを渡すことができ、   それぞれ渡されたのと同じ順序で検証が実行されます。

class Invoice < ActiveRecord::Base
  validate :expiration_date_cannot_be_in_the_past,
    :discount_cannot_be_greater_than_total_value

  def expiration_date_cannot_be_in_the_past
    if expiration_date.present? && expiration_date < Date.today
      errors.add(:expiration_date, "can't be in the past")
    end
  end

  def discount_cannot_be_greater_than_total_value
    if discount > total_value
      errors.add(:discount, "can't be greater than total value")
    end
  end
end

デフォルトでは、valid?が呼び出される度に、このような検証が毎回実行されます。 また、これらのカスタム検証を実行する際に、:onオプションに:createまたは:updateを使用して、 制御することも可能です。

class Invoice < ActiveRecord::Base
  validate :active_customer, on: :create

  def active_customer
    errors.add(:customer_id, "is not active") unless customer.active?
  end
end

7. 検証のエラーの動作

これまでに説明してきたvalid?とinvalid?に加え、   Railsには、errorsコレクションのための作業や、オブジェクトの妥当性などを調べるための多くのメソッドが用意されています。

以降に、一般的によく使用されているメソッドについて説明していきます。 全ての利用可能なメソッドを確認したい場合は、ActiveModel::Errorsドキュメントを参照してください。

7.1 errors

全てのエラーを含むActiveModel::Errorsクラスのインスタンスを返します。 各キーは属性名で、値は全エラーメッセージ文字列の配列になっています。

class Person < ActiveRecord::Base
  validates :name, presence: true, length: { minimum: 3 }
end

person = Person.new
person.valid? # => false
person.errors
 # => {:name=>["can't be blank", "is too short (minimum is 3 characters)"]}

person = Person.new(name: "John Doe")
person.valid? # => true
person.errors # => []

7.2 errors[]

errors[]は、特定の属性のエラーメッセージを調べたい場合に使用されます。 指定した属性に対する全てのエラーメッセージの配列が返されます。 もし、指定した属性にエラーが無ければ、空の配列が返されます。

class Person < ActiveRecord::Base
  validates :name, presence: true, length: { minimum: 3 }
end

person = Person.new(name: "John Doe")
person.valid? # => true
person.errors[:name] # => []

person = Person.new(name: "JD")
person.valid? # => false
person.errors[:name] # => ["is too short (minimum is 3 characters)"]

person = Person.new
person.valid? # => false
person.errors[:name]
 # => ["can't be blank", "is too short (minimum is 3 characters)"]

7.3 errors.add

addメソッドは、特定の属性に関連するメッセージの追加をさせてくれます。 errors.full_messagesまたは、errors.to_aメソッドを使用して、 ビューのフォーム内にユーザへのメッセージを表示させることが可能です。 個々のメッセージは、属性名を先頭に配置します。(且つ、語頭を大文字にします) addは、メッセージを追加したい属性名と、そのメッセージを受け取ります。

class Person < ActiveRecord::Base
  def a_method_used_for_validation_purposes
    errors.add(:name, "cannot contain the characters !@#%*()_-+=")
  end
end

person = Person.create(name: "!@#")

person.errors[:name]
 # => ["cannot contain the characters !@#%*()_-+="]

person.errors.full_messages
 # => ["Name cannot contain the characters !@#%*()_-+="]

[]= setterで同様の事が出来ます。

class Person < ActiveRecord::Base
  def a_method_used_for_validation_purposes
    errors[:name] = "cannot contain the characters !@#%*()_-+="
  end
end

person = Person.create(name: "!@#")

person.errors[:name]
 # => ["cannot contain the characters !@#%*()_-+="]

person.errors.to_a
 # => ["Name cannot contain the characters !@#%*()_-+="]

7.4 errors[:base]

特定の属性ではなく、オブジェクトの状態全体に関連づいたエラーメッセージを追加することが可能です。 属性の値に気にすることなく、このメソッドを使用して、このオブジェクトは不正であるとすることが可能です。 errors[:base]は配列であるため、単に文字列を追加することで、エラメッセージとして使用されます。

class Person < ActiveRecord::Base
  def a_method_used_for_validation_purposes
    errors[:base] << "This person is invalid because ..."
  end
end

7.5 errors.clear

clearメソッドは、故意にerrorsコレクションのメッセージを全て消去したい場合に使用されます。 もちろん、errors.clearを呼び出すことは、不当なオブジェクトを正当なものにするわけではなく、 errorsコレクションをその時点では空にしますが、その後にvalid?または他のメソッドを呼んで、 このオブジェクトをデータベースに保存しようとすると、再び検証が実行されます。 もし、検証でエラーが出れば、再びerrorsコレクションにメッセージが追加されます。

class Person < ActiveRecord::Base
  validates :name, presence: true, length: { minimum: 3 }
end

person = Person.new
person.valid? # => false
person.errors[:name]
 # => ["can't be blank", "is too short (minimum is 3 characters)"]

person.errors.clear
person.errors.empty? # => true

p.save # => false

p.errors[:name]
# => ["can't be blank", "is too short (minimum is 3 characters)"]

7.6 errors.size

sizeメソッドは、オブジェクトのエラーメッセージの総数を返します。

class Person < ActiveRecord::Base
  validates :name, presence: true, length: { minimum: 3 }
end

person = Person.new
person.valid? # => false
person.errors.size # => 2

person = Person.new(name: "Andrea", email: "[email protected]")
person.valid? # => true
person.errors.size # => 0

8. ビューへの検証エラーの表示

検証を追加されたモデルに対し、Webフォームを介してモデルのインスタンスが作成され、それを検証した結果に1でもエラーがある場合、 画面にエラーメッセージを表示したいと考えるはずです。

アプリケーション毎に、この種の扱いが異なるため、 Railsはビューヘルパーに直接これらのメッセージを生成するような機能を持たせていませんが、 Railsは豊富なメソッドを持つため、最終的にはそれを容易に実現させてくれます。 加えてscaffoldで生成すると、Railsは_form.html.erb内に、 モデルの全てのerrorsのメッセージを表示するコードを生成します。

仮に、保存対象となるモデルのインスタンス変数@postがある場合、次のようになります。

<% if @post.errors.any? %>
  <div id="error_explanation">
    <h2><%= pluralize(@post.errors.count, "error") %> prohibited this post from being saved:</h2>

    <ul>
    <% @post.errors.full_messages.each do |msg| %>
      <li><%= msg %></li>
    <% end %>
    </ul>
  </div>
<% end %>

更に、Railsのフォームヘルパーでフォームを作成した場合、 検証でエラーが発生すると、入力欄を下記のように囲う<div>を生成します。

<div class="field_with_errors">
 <input id="post_title" name="post[title]" size="30" type="text" value="">
</div>

このDIVに対し、好きなようにスタイルを指定することも出来ます。 デフォルトのscaffoldでは、Railsは例として下記のようなCSSを適用します。

.field_with_errors {
  padding: 2px;
  background-color: red;
  display: table;
}

このスタイルは、エラーのあった入力欄を2pxの赤いボーダーで囲います。

 Back to top

© 2010 - 2017 STUDIO KINGDOM