Active Record バリデーション

このガイドでは、Active Recordのバリデーション(検証: validation)機能を使って、オブジェクトがデータベースに保存される前にオブジェクトの状態を検証する方法について説明します。

このガイドの内容:

  • ビルトインのActive Recordバリデーションヘルパーの利用法
  • カスタムのバリデーションメソッドの作成
  • バリデーションプロセスで生成されたエラーメッセージの取り扱い

1 バリデーションの概要

きわめてシンプルなバリデーションの例を以下に紹介します。

class Person < ApplicationRecord
  validates :name, presence: true
end
irb> Person.create(name: "John Doe").valid?
=> true
irb> Person.create(name: nil).valid?
=> false

上でわかるように、このバリデーションはPersonname属性がない場合に無効であることを知らせます。2つ目のPersonはデータベースに保存されません。

バリデーションの詳細を説明する前に、アプリケーション全体においてバリデーションがいかに重要であるかについて説明します。

1.1 バリデーションを行なう理由

バリデーションは、正しいデータだけをデータベースに保存するために行われます。たとえば、自分のアプリケーションで、すべてのユーザーには必ず電子メールアドレスと住所が必要だとします。正しいデータだけをデータベースに保存するのであれば、モデルレベルでバリデーションを実行するのが最適です。モデルレベルでのバリデーションは、データベースに依存せず、エンドユーザーがバイパスすることもできず、テストもメンテナンスもやりやすいためです。Railsではバリデーションを簡単に利用できるよう、一般に利用可能なビルトインヘルパーが用意されており、独自のバリデーションメソッドも作成できるようになっています。

データをデータベースに保存する前にバリデーションを実行する方法は、他にもデータベースネイティブの制約機能、クライアント側でのバリデーション、コントローラレベルのバリデーションなど、さまざまです。それぞれのメリットとデメリットは以下のとおりです。

  • 「データベース制約」や「ストアドプロシージャ」を使うと、バリデーションのメカニズムがデータベースに依存してしまい、テストや保守がその分面倒になります。ただし、データベースが(Rails以外の)他のアプリケーションからも使われるのであれば、データベースレベルである程度のバリデーションを行なっておくのはよい方法です。また、データベースレベルのバリデーションの中には、利用頻度がきわめて高いテーブルの一意性バリデーションなど、他の方法では実装が困難なものもあります。
  • 「クライアント側でのバリデーション」は扱いやすく便利ですが、一般に単独では信頼性が不足します。JavaScriptを使ってバリデーションを実装する場合、ユーザーがJavaScriptをオフにするとバイパスされてしまいます。ただし、他の方法と併用するのであれば、クライアント側でのバリデーションはユーザーに即座にフィードバックを返すための便利な方法となるでしょう。
  • 「コントローラレベルのバリデーション」は一度はやってみたくなるものですが、たいてい手に負えなくなり、テストも保守も困難になりがちです。アプリケーションの寿命をのばし、保守作業を苦痛なものにしないためには、コントローラのコード量は可能な限り減らすべきです。

上で紹介したその他のバリデーションについては、特定の状況に応じて適宜追加してください。Railsチームは、ほとんどの場合モデルレベルのバリデーションが最も適切であると考えています。

1.2 バリデーション実行時の動作

Active Recordのオブジェクトには2つの種類があります。オブジェクトがデータベースの行(row)に対応しているものと、そうでないものです。たとえば、newメソッドで新しくオブジェクトを作成しただけでは、オブジェクトはデータベースに属していません。saveメソッドを呼ぶことで、オブジェクトは適切なデータベースのテーブルに保存されます。Active Recordのnew_record?インスタンスメソッドを使うと、オブジェクトが既にデータベース上にあるかどうかを確認できます。 次の単純なActive Recordクラスを例に取ってみましょう。

class Person < ApplicationRecord
end

bin/rails consoleの出力で様子を観察してみます。

irb> p = Person.new(name: "John Doe")
=> #<Person id: nil, name: "John Doe", created_at: nil, updated_at: nil>

irb> p.new_record?
=> true

irb> p.save
=> true

irb> p.new_record?
=> false

新規レコードを作成して保存すると、SQLのINSERT操作がデータベースに送信されます。既存のレコードを更新すると、SQLのUPDATE操作が送信されます。バリデーションは、SQLのデータベースへの送信前に行うのが普通です。バリデーションのいずれかが失敗すると、オブジェクトは無効(invalid)とマークされ、Active RecordでのINSERTUPDATE操作は行われません。これにより、無効なオブジェクトがデータベースに保存されないようにします。オブジェクトの作成、保存、更新時に特定のバリデーションを実行することもできます。

データベース上のオブジェクトの状態を変える方法が多数あることにご注意ください。 メソッドには、バリデーションをトリガするものと、しないものがあります。この点に注意しておかないと、バリデーションが設定されているにもかかわらず、データベース上のオブジェクトが無効な状態になってしまう可能性があります。

以下のメソッドではバリデーションがトリガされ、オブジェクトが有効な場合にのみデータベースに保存されます。

  • create
  • create!
  • save
  • save!
  • update
  • update!

!が末尾に付く破壊的メソッド(save!など)では、レコードが無効な場合に例外が発生します。 非破壊的なメソッドは、無効な場合に例外を発生しません。saveupdateは無効な場合にfalseを返し、createは無効な場合に単にそのオブジェクトを返します。

1.3 バリデーションのスキップ

以下のメソッドはバリデーションを行わずにスキップします。オブジェクトの保存は、有効無効にかかわらず行われます。これらのメソッドの利用には注意が必要です。

  • decrement!
  • decrement_counter
  • increment!
  • increment_counter
  • insert
  • insert!
  • insert_all
  • insert_all!
  • toggle!
  • touch
  • touch_all
  • update_all
  • update_attribute
  • update_column
  • update_columns
  • update_counters
  • upsert
  • upsert_all

実は、savevalidate: falseを引数として与えると、saveのバリデーションをスキップすることが可能です。この手法は十分注意して使う必要があります。

  • save(validate: false)

1.4 valid?invalid?

Railsは、Active Recordオブジェクトを保存する直前にバリデーションを実行します。バリデーションで何らかのエラーが発生すると、オブジェクトを保存しません。

valid?メソッドを使って、バリデーションを手動でトリガすることもできます。valid?を実行するとバリデーションがトリガされ、オブジェクトにエラーがない場合はtrueを返し、エラーの場合はfalseを返します。 これは以下のように実装できます。

class Person < ApplicationRecord
  validates :name, presence: true
end
irb> Person.create(name: "John Doe").valid?
=> true
irb> Person.create(name: nil).valid?
=> false

Active Recordでバリデーションが行われた後でerrorsインスタンスメソッドを使うと、失敗したバリデーションにアクセスできます。このメソッドはエラーのコレクションを返します。 その名の通り、バリデーション実行後にコレクションが空である場合はオブジェクトは有効です。

ただし、newでインスタンス化されたオブジェクトは、それが厳密には無効であってもエラーは出力されないので、注意が必要です。これは、createsaveメソッドなどによってオブジェクトが保存されるときのみ、バリデーションが自動的に実行されるためです。

class Person < ApplicationRecord
  validates :name, presence: true
end
irb> p = Person.new
=> #<Person id: nil, name: nil>
irb> p.errors.size
=> 0

irb> p.valid?
=> false
irb> p.errors.objects.first.full_message
=> "Name can't be blank"

irb> p = Person.create
=> #<Person id: nil, name: nil>
irb> p.errors.objects.first.full_message
=> "Name can't be blank"

irb> p.save
=> false

irb> p.save!
ActiveRecord::RecordInvalid: Validation failed: Name can't be blank

irb> Person.create!
ActiveRecord::RecordInvalid: Validation failed: Name can't be blank

invalid?valid?と逆のチェックを行います。このメソッドはバリデーションをトリガし、オブジェクトでエラーが発生した場合はtrueを返し、エラーがない場合はfalseを返します。

1.5 errors[]

errors[:attribute]を使うと、特定のオブジェクトの属性が有効かどうかを確認できます。このメソッドは、:attributeのすべてのエラーの配列を返します。指定された属性でエラーが発生しなかった場合は、空の配列が返されます。

このメソッドが役に立つのは、バリデーションを実行しただけです。このメソッドはエラーのコレクションを調べるだけで、バリデーションそのものをトリガしないからです。このメソッドは、前述のActiveRecord::Base#invalid?メソッドとは異なります。このメソッドはオブジェクト全体の正当性については確認せず、オブジェクトの個別の属性についてエラーがあるかどうかだけを調べます。

class Person < ApplicationRecord
  validates :name, presence: true
end
irb> Person.new.errors[:name].any?
=> false
irb> Person.create.errors[:name].any?
=> true

より高レベルなバリデーションエラーについては、バリデーションエラーの取り扱いセクションを参照してください。

2 バリデーションヘルパー

Active Recordには、クラス定義の内側で直接使える定義済みのバリデーションヘルパーが多数用意されています。これらのヘルパーは、共通のバリデーションルールを提供します。バリデーションが失敗するたびに、オブジェクトのerrorsコレクションにエラーメッセージが追加され、そのメッセージは、バリデーションが行われる属性に関連付けられます。

どのヘルパーも任意の数の属性を受け付けることができるので、1行のコードを書くだけで多くの属性に対して同じバリデーションを実行できます。

:onオプションと:messageオプションはどのヘルパーでも使えます。:onオプションはバリデーションを実行するタイミングを指定し、:messageオプションはバリデーション失敗時にerrorsコレクションに追加するメッセージを指定します。:onオプションは:createまたは:updateのいずれかの値を取ります。バリデーションヘルパーには、それぞれデフォルトのエラーメッセージが用意されています。:messageオプションが使われていない場合はデフォルトのメッセージが使われます。利用可能なヘルパーを1つずつ見ていきましょう。

利用可能なデフォルトヘルパーのリストについては、 ActiveModel::Validations::HelperMethodsを参照してください。

2.1 acceptance

このメソッドは、フォームが送信されたときにユーザーインターフェイス上のチェックボックスがオンになっているかどうかを検証します。ユーザーによるサービス利用条項への同意が必要な場合や、ユーザーが何らかの文書に目を通したことを確認させる場合によく使われます。

class Person < ApplicationRecord
  validates :terms_of_service, acceptance: true
end

このチェックは、terms_of_servicenilでない場合にのみ実行されます。 このヘルパーのデフォルトエラーメッセージは「must be accepted」です。 次のようにカスタムメッセージをmessageオプションで渡すこともできます。

class Person < ApplicationRecord
  validates :terms_of_service, acceptance: { message: 'must be abided' }
end

このヘルパーでは:acceptオプションも渡せます。このオプションは、「同意可能(acceptable)」とみなす値を指定します。デフォルトは['1', true]ですが、変更は簡単です。

class Person < ApplicationRecord
  validates :terms_of_service, acceptance: { accept: 'yes' }
  validates :eula, acceptance: { accept: ['TRUE', 'accepted'] }
end

これはWebアプリケーション特有のバリデーションであり、データベースに保存する必要はありません。これに対応するフィールドがなくても、単にヘルパーが仮想の属性を作成してくれます。このフィールドがデータベースに存在すると、acceptオプションを設定するかtrueを指定しなければならず、そうでない場合はバリデーションが実行されなくなります。

2.2 confirmation

このヘルパーは、2つのテキストフィールドで受け取る内容が完全に一致する必要がある場合に使います。たとえば、メールアドレスやパスワードで、確認フィールドを使うとします。このバリデーションヘルパーは仮想の属性を作成します。属性の名前は、確認したい属性名に「_confirmation」を追加したものになります。

class Person < ApplicationRecord
  validates :email, confirmation: true
end

ビューテンプレートで以下のようなフィールドを用意します。

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

このチェックは、email_confirmationnilでない場合のみ行われます。確認を必須にするには、以下のように確認用の属性について存在チェックも追加してください。presenceを利用する存在チェックについてはこの後解説します。

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

:case_sensitiveオプションを用いて、大文字小文字の違いを確認する制約をかけるかどうかも定義できます。デフォルトでは、このオプションはtrueになります。

class Person < ApplicationRecord
  validates :email, confirmation: { case_sensitive: false }
end

このヘルパーのデフォルトメッセージは「doesn't match confirmation」です。 messageオプションでカスタムメッセージを渡すことも可能です。

このバリデーターを使う場合は、:ifオプションと組み合わせて、レコードを保存するたびに「_confirmation」フィールドをバリデーションするのではなく、初期フィールドが変更されたときのみバリデーションするのが一般的です。詳しくは条件付きバリデーションで後述します。

class Person < ApplicationRecord
  validates :email, confirmation: true
  validates :email_confirmation, presence: true, if: :email_changed?
end

2.3 comparison

このチェックは、比較可能な2つの値の比較を検証します。

class Promotion < ApplicationRecord
  validates :end_date, comparison: { greater_than: :start_date }
end

このヘルパーのデフォルトのエラーメッセージは"failed comparison"です。 messageオプションでカスタムメッセージを渡すことも可能です。

サポートされているオプションは以下のとおりです。

  • :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}」です。
  • :other_than: 渡された値と異なる値でなければならないことを指定します。デフォルトのエラーメッセージは「must be other than %{count}」です。

2.4 format

このヘルパーは、withオプションで与えられた正規表現と属性の値がマッチするかどうかを検証します。

class Product < ApplicationRecord
  validates :legacy_code, format: { with: /\A[a-zA-Z]+\z/,
    message: "英文字のみが使えます" }
end

逆に、:withoutオプションを使うと、指定の属性が正規表現にマッチしないことを要求できます。

どちらの場合も、指定する:with:withoutオプションは、正規表現か、正規表現を返すprocまたはlambdaでなければなりません。

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

文字列の冒頭や末尾にマッチさせるためにはA\zを使い、^$は、1行の冒頭や末尾にマッチさせる場合に使うこと。A\zを使うべき場合に^$を使う誤用が頻発しているため、^$を使う場合はmultiline: trueオプションを渡す必要があります。ほとんどの場合、必要なのは\A\zです。

2.5 inclusion

このヘルパーは、指定の集合に属性の値が含まれているかどうかを検証します。集合には任意のenumerableオブジェクトが使えます。

class Coffee < ApplicationRecord
  validates :size, inclusion: { in: %w(small medium large),
message: "%{value} のサイズは無効です" }
end

inclusionヘルパーには:inオプションがあり、受け付ける値の集合を指定します。:inオプションには:withinというエイリアスもあり、好みに応じてどちらでも使えます。上の例では、属性の値をインクルードする方法を示すために:messageオプションも使っています。完全なオプションについては、:messageのドキュメントを参照してください。

このヘルパーのデフォルトのエラーメッセージは「is not included in the list」です。

2.6 exclusion

inclusionの逆は、exclusionです!

このヘルパーは、指定の集合に属性の値が「含まれていない」ことをバリデーションします。集合には任意のenumerableオブジェクトが使えます。

class Account < ApplicationRecord
  validates :subdomain, exclusion: { in: %w(www us ca jp),
    message: "%{value}は予約済みです" }
end

exclusionヘルパーの:inオプションには、バリデーションを行った属性の値に含めたくない値の集合を指定します。:inオプションには:withinというエイリアスもあり、好みに応じてどちらでも使えます。上の例では、:messageオプションを使って属性の値を含める方法を示しています。message引数の完全なオプションについては、:messageのドキュメントを参照してください。

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

または、伝統的なenumerable(Arrayなど)の代わりに、enumerableを返すproc、lambda、またはシンボルを指定することも可能です。 このバリデーションは、enumerableが数値や時間や日付の「範囲」である場合はRange#cover?で行われ、それ以外の場合はinclude?で行われます。procやlambdaを使う場合は、そのインスタンスがバリデーション時に引数として渡されます。

2.7 length

このヘルパーは、属性の値の長さを検証します。多くのオプションがあり、さまざまな長さ制限を指定できます。

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

利用できる長さ制限オプションは以下のとおりです。

  • :minimum: 属性はこの値より小さな値を取れません。
  • :maximum: 属性はこの値より大きな値を取れません。
  • :inまたは:within: 属性の長さは、与えられた区間以内でなければなりません。このオプションの値は範囲でなければなりません。
  • :is: 属性の長さは与えられた値と等しくなければなりません。

デフォルトのエラーメッセージは、実行されるバリデーションの種類によって異なります。デフォルトのメッセージは:wrong_length:too_long:too_shortオプションを使ってカスタマイズすることも、%{count}を長さ制限に対応する数値のプレースホルダにも使えます。:messageオプションを使ってエラーメッセージを指定することもできます。

class Person < ApplicationRecord
  validates :bio, length: { maximum: 1000,
    too_long: "最大%{count}文字まで使えます" }
end

デフォルトのエラーメッセージは英語が複数形で表現されていることにご注意ください(例: "is too short (minimum is %{count} characters)")。このため、:minimumを1に設定するのであれば、メッセージをカスタマイズして単数形にするか、代わりにpresence: trueを使います。:inまたは:withinの下限に1を指定する場合、メッセージをカスタマイズして単数形にするか、lengthより先にpresenceを呼ぶようにします。

制約オプションは一度に1つしか利用できませんが、:minimum:maximumオプションは組み合わせて使えます。

2.8 numericality

このヘルパーは、属性に数値のみが使われていることをバリデーションします。デフォルトでは、整数値または浮動小数点数値にマッチします。これらの冒頭に符号がある場合もマッチします。

値として整数のみを許すことを指定するには、:only_integertrueに設定します。これにより、属性の値に対するバリデーションで以下の正規表現が使われます。

/\A[+-]?\d+\z/

それ以外の場合は、Floatを用いる数値への変換を試みます。Floatは、カラムの精度または最大15桁を用いてBigDecimalにキャストされます。

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

:only_integerのデフォルトのエラーメッセージは「must be an integer」です。

このヘルパーには、:only_integerの他に:only_numericオプションも渡せます。これは、値がNumericのインスタンスでなければならないことを指定し、値がStringの場合は値の解析を試みます。

デフォルトでは、numericalityオプションでnil値は許容されません。nil値を許可するにはallow_nil: trueオプションを使ってください。IntegerカラムやFloatカラムでは、空の文字列がnilに変換される点にご注意ください。

オプションが指定されていない場合のデフォルトのエラーメッセージは「is not a number」です。

このヘルパーでは、: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}」です。
  • :other_than: 指定の値以外の値でなければならないことを指定します。デフォルトのエラーメッセージは「must be other than %{count}」です。
  • :in: 渡された範囲に値が含まれていなければならないことを指定します。デフォルトのエラーメッセージは「must be in %{count}」です。
  • :odd: trueの場合は奇数でなければなりません。デフォルトのエラーメッセージは「must be odd」です。
  • :even: trueの場合は偶数でなければなりません。デフォルトのエラーメッセージは「must be even」です。

2.9 presence

このヘルパーは、指定された属性が空(empty)でないことを確認します。値がnilや空文字でない、つまり空でもなければホワイトスペースでもないことを確認するために、内部でObject#blank?メソッドを使っています。

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

関連付けが存在することを確認したい場合、関連をマッピングするために使われる外部キーではなく、関連するオブジェクト自体が存在するかどうかをバリデーションする必要があります。以下の例では、外部キーが空ではないことと、関連付けられたオブジェクトが存在することをチェックしています。

class Supplier < ApplicationRecord
  has_one :account
  validates :account, presence: true
end

関連付けられたレコードの存在が必須の場合、これをバリデーションするには:inverse_ofオプションでその関連付けを指定する必要があります。

class Order < ApplicationRecord
  has_many :line_items, inverse_of: :order
end

関連付けが存在し、かつ有効であることを確認したい場合は、validates_associatedも使う必要があります。詳しくは後述します

このヘルパーを使って、has_oneまたはhas_manyリレーションシップを経由して関連付けられたオブジェクトが存在することを検証すると、blank?でもなくmarked_for_destruction?(削除用マーク済み)でもないかどうかがチェックされます。

false.blank?は常にtrueなので、真偽値に対してこのメソッドを使うと正しい結果が得られません。真偽値の存在をチェックしたい場合は、以下のいずれかを使う必要があります。

# 値はtrueかfalseでなければならない
validates :boolean_field_name, inclusion: [true, false]
# 値はnilであってはならない、すなわちtrueかfalseでなければならない
validates :boolean_field_name, exclusion: [nil]

これらのバリデーションのいずれかを使うことで、値が決してnilにならないようにできます。nilがあると、ほとんどの場合NULL値になります。

デフォルトのエラーメッセージは「can't be blank」です。

2.10 absence

このヘルパーは、指定された属性が空(empty)であることを検証します。値がnilまたは空文字である、つまり空またはホワイトスペースであることを確認するために、内部でObject#present?メソッドを使っています。

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

関連付けが存在しないことを確認したい場合、関連をマッピングするのに使われる外部キーではなく、関連するオブジェクト自体が存在しないかどうかを検証する必要があります。

class LineItem < ApplicationRecord
  belongs_to :order
  validates :order, absence: true
end

関連付けられたレコードが存在してはならない場合、これを検証するには:inverse_ofオプションでその関連付けを指定する必要があります。

class Order < ApplicationRecord
  has_many :line_items, inverse_of: :order
end

関連付けが存在し、かつ有効であることを確認したい場合は、validates_associatedも使う必要があります。詳しくは後述します

このヘルパーを使って、has_oneまたはhas_manyリレーションシップを経由して関連付けられたオブジェクトが存在しないことを検証すると、presence?でもなくmarked_for_destruction?(削除用マーク済み)でもないかどうかがチェックされます。

false.present?は常にfalseなので、真偽値に対してこのメソッドを使うと正しい結果が得られません。真偽値が存在しないことをチェックしたい場合は、validates :field_name, exclusion: { in: [true, false] }を使う必要があります。

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

2.11 uniqueness

このヘルパーは、オブジェクトが保存される直前に、属性の値が一意(unique)であり重複していないことを検証します。

class Account < ApplicationRecord
  validates :email, uniqueness: true
end

このバリデーションは、その属性と同じ値を持つ既存のレコードがモデルのテーブルにあるかどうかを調べるSQLクエリを実行することで行われます。

このヘルパーには、一意性チェックの範囲を限定する別の属性を指定する:scopeオプションがあります。

class Holiday < ApplicationRecord
  validates :name, uniqueness: { scope: :year,
    message: "発生は年に1度である必要があります" }
end

このバリデーションはデータベースに一意性制約(uniqueness constraint)を作成しないので、異なる2つのデータベース接続で、一意であることを意図したカラムに同じ値を持つレコードが2つ作成される可能性があります。これを避けるには、データベースでそのカラムにuniqueインデックスを作成する必要があります。

データベースに一意性データベース制約を追加するには、マイグレーションで add_index ステートメントを使ってunique: trueオプションを指定します。

:scopeを用いる一意性バリデーション違反を防止する目的でデータベース側に制約を作成したい場合は、データベース側で両方のカラムにuniqueインデックスを作成しなければなりません。MySQLのマニュアルでマルチカラムインデックスについての情報を参照するか、PostgreSQLのマニュアルでカラムのグループを参照する一意性制約についての例を参照してください。

このヘルパーには:case_sensitiveというオプションもあります。これは一意性制約で大文字小文字を区別するか、またはデータベースのデフォルトの照合順序(collation)を尊重すべきかどうかを定義できます。このオプションはデフォルトで、データベースのデフォルト照合順序を尊重します。

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

一部のデータベースでは検索で常に大文字小文字を区別しない設定になっているものがあります。

:conditionsオプションを使うと、一意性制約の探索を制限するための追加条件をWHERE SQLフラグメントとして指定可能です(例: conditions: -> { where(status: 'active') })。

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

2.12 validates_associated

常に有効でなければならない関連付けがモデルにある場合は、このヘルパーを使う必要があります。オブジェクトを保存しようとするたびに、関連するオブジェクトごとにvalid?が呼び出されます。

class Library < ApplicationRecord
  has_many :books
  validates_associated :books
end

このバリデーションは、すべての種類の関連付けで機能します。

validates_associatedを関連付けの両側で使ってはいけません。互いを呼び出して無限ループになります。

validates_associated のデフォルトのエラーメッセージは「is invalid」です。各関連付けオブジェクトには、それ自身のerrorsコレクションも含まれることに注意してください。エラーは呼び出し元のモデルには達しません。

validates_associatedはActive Recordオブジェクトでしか利用できませんが、従来のバリデーションはActiveModel::Validationsを含む任意のオブジェクトでも利用できます。

2.13 validates_each

このヘルパーは、ブロックに対して属性をバリデーションします。このヘルパーは、事前定義されたバリデーション関数を持っていません。ブロックで作成されてvalidates_eachに渡されたすべての属性は、そのブロックに対してテストされます。

以下の例は、小文字で始まる名前と姓を却下します。

class Person < ApplicationRecord
  validates_each :name, :surname do |record, attr, value|
    record.errors.add(attr, '大文字で始まる必要があります') if /\A[[:lower:]]/.match?(value)
  end
end

このブロックは、レコード、属性名、属性の値を受け取ります。

ブロック内のデータが有効かどうかのチェックには任意のコードを書けます。バリデーションに失敗した場合は、モデルにエラーを追加して無効とする必要があります。

2.14 validates_with

このヘルパーは、バリデーション専用の別クラスにレコードを渡します。

class GoodnessValidator < ActiveModel::Validator
  def validate(record)
    if record.first_name == "Evil"
      record.errors.add :base, "これは悪人だ"
    end
  end
end

class Person < ApplicationRecord
  validates_with GoodnessValidator
end

validates_withにはデフォルトのエラーメッセージがありません。バリデータークラスのレコードのエラーコレクションに、手動でエラーを追加する必要があります。

record.errors[:base]には、そのレコード全体のステートに関連するエラーメッセージを追加するのが一般的です。

バリデーションメソッドを実装するには、メソッド定義内にrecordパラメータが必要です。このパラメータはバリデーションを行なうレコードを表します。

特定の属性に関するエラーを追加したい場合は、record.errors.add(:first_name, "please choose another name")のように第1引数にその属性を渡します。詳しくはバリデーションエラーで後述します。

def validate(record)
  if record.some_field != "acceptable"
    record.errors.add :some_field, "this field is unacceptable"
  end
end

validates_withヘルパーは、バリデーションに使うクラス(またはクラスのリスト)を引数に取ります。

class Person < ApplicationRecord
  validates_with MyValidator, MyOtherValidator, on: :create
end

他のバリデーションと同様、validates_withヘルパーでも:if:unless:onオプションが使えます。その他のオプションは、バリデータクラスにoptionsとして渡されます。

class GoodnessValidator < ActiveModel::Validator
  def validate(record)
    if options[:fields].any? { |field| record.send(field) == "Evil" }
      record.errors.add :base, "これは悪人だ"
    end
  end
end

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

このバリデータは、アプリケーションのライフサイクル内で一度しか初期化されない点にご注意ください。バリデーションが実行されるたびに初期化されることはありません。インスタンス変数を使う場合は十分な注意が必要です。

作成したバリデータが複雑になってインスタンス変数を使いたくなった場合は、代わりに素のRubyオブジェクトを使う方がやりやすいでしょう。

class Person < ApplicationRecord
  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.add :base, "これは悪人だ"
    end
  end

  # ...
end

詳しくはカスタムバリデーションで後述します。

3 バリデーションの共通オプション

これまで見てきたバリデータにはさまざまな共通オプションがあるので、そのいくつかを以下に示します。

これらのオプションは、すべてのバリデータでサポートされているとは限りません。詳しくはActiveModel::Validationsの API ドキュメントを参照してください。

上述のバリデーションの方法を使う場合、バリデータ間で共通して使えるオプションのリストも存在します。

  • :allow_nil: 属性がnilの場合にバリデーションをスキップする。
  • :allow_blank: 属性がblankの場合にバリデーションをスキップする。
  • :message: カスタムのエラーメッセージを指定する。
  • :on: このバリデーションを有効にするコンテキストを指定する。
  • :strict: バリデーション失敗時にraiseする。
  • :if:unless: バリデーションする場合やしない場合の条件を指定する。

3.1 :allow_nil

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

class Coffee < ApplicationRecord
  validates :size, inclusion: { in: %w(small medium large),
    message: "%{value}は有効な値ではありません" }, allow_nil: true
end
irb> Coffee.create(size: nil).valid?
=> true
irb> Coffee.create(size: "mega").valid?
=> false

message:引数の完全なオプションについては、:messageのドキュメントを参照してください。

3.2 :allow_blank

:allow_blankオプションは:allow_nilオプションと似ています。このオプションを指定すると、属性の値がblank?に該当する場合(nilや空文字列など)にバリデーションがパスします。

class Topic < ApplicationRecord
  validates :title, length: { is: 5 }, allow_blank: true
end
irb> Topic.create(title: "").valid?
=> true
irb> Topic.create(title: nil).valid?
=> true

3.3 :message

既に例示したように、:messageオプションを使うことで、バリデーション失敗時にerrorsコレクションに追加されるカスタムエラーメッセージを指定できます。このオプションを使わない場合、Active Recordはバリデーションヘルパーごとにデフォルトのエラーメッセージを使います。

:messageオプションはStringまたはProcを値として受け取ります。

String:message値には、%{value}%{attribute}%{model}をオプションで含められます。これらはバリデーション失敗時に動的に置き換えられます。置き換えはi18n gemで行われます。プレースホルダは正確にマッチしなければならず、スペースは許されません。

class Person < ApplicationRecord
  # メッセージを直書きする場合
  validates :name, presence: { message: "省略できません" }

  # 動的な属性値を含むメッセージの場合。%{value}は実際の属性値に
  # 置き換えられる。%{attribute}や%{model}も利用可能。
  validates :age, numericality: { message: "%{value}は誤りかもしれません" }
end

Proc:message値は引数を2つ受け取ります。バリデーションの対象となるオブジェクトと、:model:attribute:valueのキーバリューペアを含むハッシュです。

class Person < ApplicationRecord
  validates :username,
    uniqueness: {
      # object = バリデーションされる人物のオブジェクト
      # data = { model: "Person", attribute: "Username", value: <username> }
      message: ->(object, data) do
        "#{object.name}さま、#{data[:value]}は既に入力済みです"
      end
    }
end

3.4 :on

:onオプションは、バリデーション実行のタイミングを指定します。ビルトインのバリデーションヘルパーは、デフォルトでは保存時(レコードの作成時および更新時の両方)に実行されます。バリデーションのタイミングを変更したい場合、on: :createを指定すればレコード新規作成時にのみバリデーションが行われ、on: :updateを指定すればレコードの更新時にのみバリデーションが行われます。

class Person < ApplicationRecord
  # 値が重複していてもemailを更新できる
  validates :email, uniqueness: true, on: :create

  # 新規レコード作成時に、数字でない年齢表現を使える
  validates :age, numericality: true, on: :update

  # デフォルト (作成時と更新時の両方でバリデーションを行なう)
  validates :name, presence: true
end

on:にはカスタムコンテキストも定義できます。カスタムコンテキストは、valid?invalid?saveにコンテキスト名を渡して明示的にトリガーする必要があります。

class Person < ApplicationRecord
  validates :email, uniqueness: true, on: :account_setup
  validates :age, numericality: true, on: :account_setup
end
irb> person = Person.new(age: 'thirty-three')
irb> person.valid?
=> true
irb> person.valid?(:account_setup)
=> false
irb> person.errors.messages
=> {:email=>["has already been taken"], :age=>["is not a number"]}

person.valid?(:account_setup)は、モデルを保存せずにバリデーションを2つとも実行します。person.save(context: :account_setup)は、保存の前にaccount_setupコンテキストでpersonをバリデーションします。

以下のようにシンボルの配列も渡せます。

class Book
  include ActiveModel::Validations

  validates :title, presence: true, on: [:update, :ensure_title]
end
irb> book = Book.new(title: nil)
irb> book.valid?
=> true
irb> book.valid?(:ensure_title)
=> false
irb> book.errors.messages
=> {:title=>["can't be blank"]}

明示的なトリガーによるモデルのバリデーションでは、そのコンテキストのみのバリデーションと、「コンテキストなし」のバリデーションが行われます。

class Person < ApplicationRecord
  validates :email, uniqueness: true, on: :account_setup
  validates :age, numericality: true, on: :account_setup
  validates :name, presence: true
end
irb> person = Person.new
irb> person.valid?(:account_setup)
=> false
irb> person.errors.messages
=> {:email=>["has already been taken"], :age=>["is not a number"], :name=>["can't be blank"]}

on:のユースケースについて詳しくは、コールバックガイドで解説します。

4 厳密なバリデーション

バリデーションを厳密にし、オブジェクトが無効だった場合にActiveModel::StrictValidationFailedが発生するようにすることもできます。

class Person < ApplicationRecord
  validates :name, presence: { strict: true }
end
irb> Person.new.valid?
ActiveModel::StrictValidationFailed: Name can't be blank

カスタム例外を:strictオプションに追加することもできます。

class Person < ApplicationRecord
  validates :token, presence: true, uniqueness: true, strict: TokenGenerationException
end
irb> Person.new.valid?
TokenGenerationException: Token can't be blank

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

特定の条件を満たす場合にのみバリデーションを実行したい場合があります。:ifオプションや:unlessオプションを使うことでこのような条件を指定できます。引数にはシンボル、ProcまたはArrayを使えます。:ifオプションは、特定の条件でバリデーションを行なうべきである場合に使います。特定の条件でバリデーションを行なうべきでない場合は、:unlessオプションを使います。

5.1 :if:unlessでシンボルを使う

バリデーションの実行直前に呼び出されるメソッド名を、:if:unlessオプションにシンボルで指定することもできます。 これは最もよく使われるオプションです。

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

  def paid_with_card?
    payment_type == "card"
  end
end

5.2 :if:unlessProcを使う

呼び出したいProcオブジェクトを:if:unlessで使うこともできます。Procオブジェクトを使うと、個別のメソッドを指定する代わりに、その場で条件を書けるようになります。ワンライナーに収まる条件を使いたい場合に最適です。

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

lambdaはProcの一種なので、lambda記法(->)を用いて以下のようにインライン条件をさらに短く書くこともできます。

validates :password, confirmation: true, unless: -> { password.blank? }

5.3 条件付きバリデーションをグループ化する

1つの条件を複数のバリデーションで共用できると便利なことがあります。これはwith_optionsで簡単に実現できます。

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

with_optionsブロックの内側にあるすべてのバリデーションにif: :is_admin?という条件が渡されます。

5.4 バリデーションの条件を組み合わせる

逆に、バリデーションを行なう条件を複数定義したい場合はArrayを使えます。さらに、1つのバリデーションに:if:unlessを両方使うこともできます。

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

このバリデーションは、:if条件がすべてtrueで、かつ:unlessが1つもtrueにならない場合にのみ実行されます。

6 カスタムバリデーションを実行する

ビルトインのバリデーションヘルパーだけでは不足の場合、好みのバリデータやバリデーションメソッドを作成して使えます。

6.1 カスタムバリデータ

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

class MyValidator < ActiveModel::Validator
  def validate(record)
    unless record.name.start_with? 'X'
      record.errors.add :name, "名前はXで始まる必要があります"
    end
  end
end

class Person
  validates_with MyValidator
end

個別の属性を検証するためのカスタムバリデータを追加するには、ActiveModel::EachValidatorを使うのが最も手軽で便利です。この場合、このカスタムバリデータクラスはvalidate_eachメソッドを実装する必要があります。このメソッドは、そのインスタンスに対応するレコード、バリデーションを行う属性、そして渡されたインスタンスの属性の値の3つの引数を取ります。

class EmailValidator < ActiveModel::EachValidator
  def validate_each(record, attribute, value)
    unless value =~ /\A([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})\z/i
      record.errors.add attribute, (options[:message] || "はメールアドレスではありません")
    end
  end
end

class Person < ApplicationRecord
  validates :email, presence: true, email: true
end

上の例に示したように、標準のバリデーションとカスタムバリデーションを組み合わせることもできます。

6.2 カスタムメソッド

モデルのステートを確認して、無効な場合にerrorsコレクションにメッセージを追加するメソッドを作成できます。これらのメソッドを作成後、validateクラスメソッドを使って登録し、バリデーションメソッド名を指すシンボルを渡す必要があります。

クラスメソッドごとに複数のシンボルを渡せます。バリデーションは登録されたとおりの順序で実行されます。

valid?メソッドはerrorsコレクションが空であることを検証するので、カスタムバリデーションはバリデーションが失敗したときにエラーを追加すべきです。

class Invoice < ApplicationRecord
  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, "過去の日付は使えません")
    end
  end

  def discount_cannot_be_greater_than_total_value
    if discount > total_value
      errors.add(:discount, "合計額を上回ることはできません")
    end
  end
end

これらのバリデーションは、デフォルトではvalid?を呼び出したりオブジェクトを保存したりするたびに実行されます。しかし:onオプションを使えば、カスタムバリデーションが実行されるタイミングを変更できます。validateに対してon: :createまたはon: :updateを指定します。

class Invoice < ApplicationRecord
  validate :active_customer, on: :create

  def active_customer
    errors.add(:customer_id, "はアクティブではありません") unless customer.active?
  end
end

:onについて詳しくは上述のセクションを参照してください。

6.3 バリデータを一覧表示する

指定したオブジェクトのバリデータをすべて調べたい場合は、validatorsを調べるだけで十分です。

たとえば、カスタムバリデータとビルトインバリデータを使った次のようなモデルがあるとします。

class Person < ApplicationRecord
  validates :name, presence: true, on: :create
  validates :email, format: URI::MailTo::EMAIL_REGEXP
  validates_with MyOtherValidator, strict: true
end

これで、以下のようにvalidatorsでPersonモデルのすべてのバリデータを一覧表示することも、validators_onで特定のフィールドをチェックすることも可能になります。

irb> Person.validators
#=> [#<ActiveRecord::Validations::PresenceValidator:0x10b2f2158
      @attributes=[:name], @options={:on=>:create}>,
     #<MyOtherValidatorValidator:0x10b2f17d0
      @attributes=[:name], @options={:strict=>true}>,
     #<ActiveModel::Validations::FormatValidator:0x10b2f0f10
      @attributes=[:email],
      @options={:with=>URI::MailTo::EMAIL_REGEXP}>]
     #<MyOtherValidator:0x10b2f0948 @options={:strict=>true}>]

irb> Person.validators_on(:name)
#=> [#<ActiveModel::Validations::PresenceValidator:0x10b2f2158
      @attributes=[:name], @options={on: :create}>]

7 バリデーションエラーに対応する

valid?メソッドやinvalid?メソッドでは、有効かどうかという概要しかわかりません。しかしerrorsコレクションにあるさまざまなメソッドを使えば、個別のエラーをさらに詳しく調べられます。

以下は最もよく使われるメソッドの一覧です。利用可能なすべてのメソッドについては、ActiveModel::Errorsドキュメントを参照してください。

7.1 errors

個別のエラーのさまざまな詳細を調べるときの入り口となります。

すべてのエラーを含むActiveModel::Errorクラスのインスタンスを1つ返します。個別のエラーは、ActiveModel::Errorオブジェクトによって表現されます。

class Person < ApplicationRecord
  validates :name, presence: true, length: { minimum: 3 }
end
irb> person = Person.new
irb> person.valid?
=> false
irb> person.errors.full_messages
=> ["名前は空欄にできません", "名前が短すぎます(最小で3文字以上)"]

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


irb> person = Person.new
irb> person.valid?
=> false
irb> person.errors.first.details
=> {:error=>:too_short, :count=>3}

7.2 errors[]

errors[]は、特定の属性についてエラーメッセージをチェックしたい場合に使います。指定の属性に関するすべてのエラーメッセージの文字列の配列を返します(1つの文字列に1つのエラーメッセージが対応します)。属性に関連するエラーがない場合は空の配列を返します。

class Person < ApplicationRecord
  validates :name, presence: true, length: { minimum: 3 }
end
irb> person = Person.new(name: "John Doe")
irb> person.valid?
=> true
irb> person.errors[:name]
=> []

irb> person = Person.new(name: "JD")
irb> person.valid?
=> false
irb> person.errors[:name]
=> ["短すぎます(最小で3文字以上)"]

irb> person = Person.new
irb> person.valid?
=> false
irb> person.errors[:name]
=> ["空欄にはできません", "短すぎます(最小で3文字以上)"]

7.3 errors.whereとエラーオブジェクト

エラーごとに、そのエラーメッセージ以外の情報が必要になることがあります。各エラーはActiveModel::Errorオブジェクトとしてカプセル化されており、それらへのアクセスに最もよく用いられるのがwhereメソッドです。

whereは、さまざまな度合いの条件でフィルタされたエラーオブジェクトの配列を返します。

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

これをerrors.where(:attr)の第1パラメータとして渡すことで、attributeだけをフィルタリングできます。 第2パラメータは、errors.where(:attr, :type)を呼び出して、エラーのtypeをフィルタリングするのに使われます。

irb> person = Person.new
irb> person.valid?
=> false

irb> person.errors.where(:name)
=> [ ... ] # :name属性のすべてのエラー

irb> person.errors.where(:name, :too_short)
=> [ ... ] # :name属性の:too_shortエラー

最後に、指定の型のエラーオブジェクトに存在する可能性のある任意のoptionsでフィルタリングできます。

irb> person = Person.new
irb> person.valid?
=> false

irb> person.errors.where(:name, :too_short, minimum: 3)
=> [ ... ] # 最小が2で短すぎるすべてのnameのエラー

これらのエラーオブジェクトから、さまざまな情報を読み出せます。

irb> error = person.errors.where(:name).last

irb> error.attribute
=> :name
irb> error.type
=> :too_short
irb> error.options[:count]
=> 3

エラーメッセージを生成することも可能です。

irb> error.message
=> "is too short (minimum is 3 characters)"
irb> error.full_message
=> "Name is too short (minimum is 3 characters)"

full_messageメソッドは、属性名の冒頭を大文字にした読みやすいメッセージを生成します(full_messageで使うフォーマットをカスタマイズする方法については、国際化(i18n)ガイドを参照してください)。

7.4 errors.add

addメソッドを使って、特定の属性に関連するエラーメッセージを手動で追加できます。このメソッドは、属性とエラーメッセージを引数として受け取ります。

class Person < ApplicationRecord
  validate do |person|
    errors.add :name, :too_plain, message: "はあまりクールじゃない"
  end
end
irb> person = Person.create
irb> person.errors.where(:name).first.type
=> :too_plain
irb> person.errors.where(:name).first.full_message
=> "Nameはあまりクールじゃない"

7.5 errors[:base]

個別の属性に関連するエラーメッセージを追加する代わりに、オブジェクトのステート全体に関連するエラーメッセージを追加することもできます。このメソッドは、属性の値にかかわらずオブジェクトが無効であることを通知したい場合に使えます。errors[:base]は配列なので、これに文字列を単に追加するだけでエラーメッセージとして使えるようになります。

class Person < ApplicationRecord
  validate do |person|
    errors.add :base, :invalid, message: "この人物は以下の理由で無効です: "
  end
end
irb> person = Person.create
irb> person.errors.where(:base).first.full_message
=> "この人物は以下の理由で無効です: "

7.6 errors.size

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

class Person < ApplicationRecord
  validates :name, presence: true, length: { minimum: 3 }
end
irb> person = Person.new
irb> person.valid?
=> false
irb> person.errors.size
=> 2

irb> person = Person.new(name: "Andrea", email: "andrea@example.com")
irb> person.valid?
=> true
irb> person.errors.size
=> 0

7.7 errors.clear

clearメソッドは、errorsコレクションに含まれるメッセージをすべてクリアしたい場合に使えます。無効なオブジェクトに対してerrors.clearメソッドを呼び出しても、オブジェクトが実際に有効になるわけではありませんのでご注意ください。errorsは空になりますが、valid?やオブジェクトをデータベースに保存しようとするメソッドが次回呼び出されたときに、バリデーションが再実行されます。そしていずれかのバリデーションが失敗すると、errorsコレクションに再びメッセージが保存されます。

class Person < ApplicationRecord
  validates :name, presence: true, length: { minimum: 3 }
end
irb> person = Person.new
irb> person.valid?
=> false
irb> person.errors.empty?
=> false

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

irb> person.save
=> false

irb> person.errors.empty?
=> false

8 バリデーションエラーをビューで表示する

モデルを作成してバリデーションを追加し、Webのフォーム経由でそのモデルが作成できるようになったら、そのモデルでバリデーションが失敗したときにエラーメッセージを表示したくなります。

エラーメッセージの表示方法はアプリケーションごとに異なるため、そうしたメッセージを直接生成するビューヘルパーはRailsに含まれていません。 しかし、Railsでは一般的なバリデーションメソッドが多数提供されているので、カスタムのメソッドを作成するのは比較的簡単です。また、生成をscaffoldで行なうと、そのモデルのエラーメッセージをすべて表示するERBがRailsによって一部の_form.html.erbファイルに追加されます。

@articleという名前のインスタンス変数に保存されたモデルがあるとすると、ビューは以下のようになります。

<% if @article.errors.any? %>
  <div id="error_explanation">
    <h2><%= pluralize(@article.errors.count, "error") %>が原因でこの記事を保存できませんでした</h2>

    <ul>
      <% @article.errors.each do |error| %>
        <li><%= error.full_message %></li>
      <% end %>
    </ul>
  </div>
<% end %>

また、フォームをRailsのフォームヘルパーで生成した場合、あるフィールドでバリデーションエラーが発生すると、そのエントリの周りに追加の<div>が自動的に生成されます。

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

この<div>タグに好みのスタイルを追加できます。Railsが生成するデフォルトのscaffoldによって、以下のCSSルールが追加されます。

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

このCSSは、エラーを含むフィールドを太さ2ピクセルの赤い枠で囲みます。

フィードバックについて

Railsガイドは GitHub の yasslab/railsguides.jp で管理・公開されております。本ガイドを読んで気になる文章や間違ったコードを見かけたら、気軽に Pull Request を出して頂けると嬉しいです。Pull Request の送り方については GitHub の README をご参照ください。

原著における間違いを見つけたら『Rails のドキュメントに貢献する』を参考にしながらぜひ Rails コミュニティに貢献してみてください 🛠💨✨

本ガイドの品質向上に向けて、皆さまのご協力が得られれば嬉しいです。

Railsガイド運営チーム (@RailsGuidesJP)

支援・協賛

Railsガイドは下記の協賛企業から継続的な支援を受けています。支援・協賛にご興味あれば協賛プランからお問い合わせいただけると嬉しいです。

  1. Star
  2. このエントリーをはてなブックマークに追加