ユーザからの入力値チェックは、一般にクライアント側とサーバ側の両方で行います。クライアント側のチェックは、例えばブラウザのJavascriptによって行うことができます。これは悪意のないユーザの操作性向上のために行います。ここでは、悪意あるユーザがJavascriptを無効にして不正なデータを送信してきた場合などに備えサーバ側でチェックを行うためのRailsのバリデーション機能の使用方法を記載します。
「Rails4のScaffolding機能でCRUDアプリケーションの骨組みを作成する方法」を参考にサンプルアプリを作成します。
$ rails new myApp
$ cd myApp/
$ rails generate scaffold myModel field1:string field2:integer field3:date field4:boolean
$ rake db:migrate
$ rails s
→ http://localhost:3000/my_models/
モデル app/models/my_model.rb に各フィールドが満たすべき条件を記述します。
class MyModel < ActiveRecord::Base
validates :field1,
presence: true # 値が空を許さない
validates :field2,
presence: true # 値が空を許さない
end
MyModelのモデルインスタンスについて、コントローラで以下の記述が可能です。field1 または field2 に値がセットされていない場合は false が返されます。
バリデーションに失敗した場合はそのエラー内容がモデルインスタンスに格納されます。以下のようにしてビューで参照できます。
app/views/my_models/_form.html.erb (scaffoldで自動生成されたファイル)
<%= form_for(@my_model) do |f| %>
<% if @my_model.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(@my_model.errors.count, "error") %> prohibited this my_model from being saved:</h2>
<ul>
<% @my_model.errors.full_messages.each do |msg| %>
<li><%= msg %></li>
<% end %>
</ul>
</div>
<% end %>
...
<% end %>
デザインのためのCSSは例えば以下のように指定できます。
app/assets/stylesheets/scaffolds.css.scss (scaffoldで自動生成されたファイル)
.field_with_errors { /* エラーのあったフォームの前後には */
padding: 2px; /* field_with_errors クラスの div タグが自動生成されます (後述) */
background-color: red;
display: table;
}
#error_explanation { /* ↑の form_for... のデザイン */
width: 450px;
border: 2px solid red;
padding: 7px;
padding-bottom: 0;
margin-bottom: 20px;
background-color: #f0f0f0;
h2 {
text-align: left;
font-weight: bold;
padding: 5px 5px 5px 15px;
font-size: 12px;
margin: -7px;
margin-bottom: 0px;
background-color: #c00;
color: #fff;
}
ul li {
font-size: 12px;
list-style: square;
}
}
エラーのあったフォームの前後には field_with_errors クラスの div タグが自動生成されます。
<div class="field">
<div class="field_with_errors"><label for="my_model_field1">Field1</label></div><br>
<div class="field_with_errors"><input type="text" value="" name="my_model[field1]" id="my_model_field1"></div>
</div>
基本形
class MyModel < ActiveRecord::Base
validates :field1,
presence: true
end
複数のフィールドに対して同じ条件を指定 ('presence' に限らない記法です)
class MyModel < ActiveRecord::Base
validates :field1, :field2,
presence: true
end
エラー時のメッセージ (@my_model.errors.full_messages) をカスタマイズ
class MyModel < ActiveRecord::Base
validates :field1,
presence: { message: I18n.locale == :ja ? 'は必須項目です' : 'needs to be set.' }
end
これは、以下のようにすることもできます。
class MyModel < ActiveRecord::Base
validates :field1,
presence: { message: I18n.t('validation.presence') }
end
config/locale/ja.yml
ja:
validation:
presence: は必須項目です
config/locale/en.yml
en:
validation:
presence: needs to be set.
基本形
class MyModel < ActiveRecord::Base
validates :field1,
length: { is: 5 } # 5文字だけを許す (日本語も判別可能)
end
基本形 その2
class MyModel < ActiveRecord::Base
validates :field1,
length: { minimum: 1, maximum: 2 } # 1-2 文字
end
別の条件もANDで指定 ('length' に限らない記法です)
class MyModel < ActiveRecord::Base
validates :field1,
length: { is: 5 },
presence: true
end
エラー時のメッセージ (@my_model.errors.full_messages) をカスタマイズ
class MyModel < ActiveRecord::Base
validates :field1,
length: { is: 5, message: '%{value} は %{count} 桁である必要があります' }
end
class MyModel < ActiveRecord::Base
validates :field1,
format: { with: /\Apattern\d+\z/ }
end
なお ^pattern\d+$
としてしまうと、複数行の以下のような文字列に合致してしまうので注意しましょう。
hoge
pattern123
なお '\z' であって '\Z' ではないので注意しましょう。
pattern123
← ここ、改行されるだけで文字列を含まない (「\Apattern\d+\Z」は合致する)
上限を指定
class MyModel < ActiveRecord::Base
validates :field1,
numericality: { only_integer: true, less_than_or_equal_to: 10 } # 数値のみ。10以下
end
下限を指定
class MyModel < ActiveRecord::Base
validates :field1,
numericality: { only_integer: true, greater_than_or_equal_to: 10 } # 数値のみ。10以上
end
含まれていることを検証
class MyModel < ActiveRecord::Base
validates :field1,
inclusion: { in: ['hoge1','hoge2'] }
end
含まれていないことを検証
class MyModel < ActiveRecord::Base
validates :field1,
exclusion: { in: ['hoge1','hoge2'] }
end
基本形
class MyModel < ActiveRecord::Base
validates :field1,
uniqueness: true # 重複を許さない
end
大文字小文字を区別せずに一意であるかどうか (条件としてはより厳しくなる)
class MyModel < ActiveRecord::Base
validates :field1,
uniqueness: { case_sensitive: false }
end
メッセージを指定
class MyModel < ActiveRecord::Base
validates :field1,
uniqueness: { message: '%{value} は既に登録済みです' }
end
複数フィールドセットで考えた場合に一意であるかどうか
class MyModel < ActiveRecord::Base
validates :field1,
# field1に重複があったとしてもfield2が異なればよしとする
uniqueness: { scope: :field2 }
end
メールアドレスやパスワードなど、間違ってもらっては困る項目を、確認のため二回入力してもらい合致することを確認します。二回目の入力のために仮想的にフィールドを追加します。データベースへの追加は不要であることに注意してください。
app/views/my_models/_form.html.erb
...
<div class="field">
<%= f.label :field1 %><br>
<%= f.text_field :field1 %>
</div>
<div class="field"> ← これを追加
<%= f.label :field1_confirmation %><br> ←
<%= f.text_field :field1_confirmation %> ←
</div> ←
...
app/controllers/my_models_controller.rb
...
# Never trust parameters from the scary internet, only allow the white list through.
def my_model_params
# 「:field1_confirmation」を追加
params.require(:my_model).permit(:field1, :field1_confirmation, :field2, :field3, :field4)
end
end
app/models/my_model.rb
class MyModel < ActiveRecord::Base
validates :field1, confirmation: true
end
ユーザの同意などのために一時的に必要になるフィールドを設置できます。
app/views/my_models/_form.html.erb
...
<div class="field">
<%= f.label :terms_of_service %><br>
<%= f.check_box :terms_of_service %>
</div>
...
app/controllers/my_models_controller.rb
...
# Never trust parameters from the scary internet, only allow the white list through.
def my_model_params
# 「:terms_of_service」を追加
params.require(:my_model).permit(:field1, :field2, :field3, :field4, :terms_of_service)
end
end
app/models/my_model.rb
class MyModel < ActiveRecord::Base
validates :terms_of_service, acceptance: true
end
値がセットされていなければ検証を通りません。なお、値がフォーム側で指定されている場合はモデル側での対応が必要です。
app/views/my_models/_form.html.erb
...
<div class="field">
<%= f.label :terms_of_service %><br>
<%= f.check_box :terms_of_service, {checked: true}, 'my_value' %>
</div>
...
app/models/my_model.rb
class MyModel < ActiveRecord::Base
validates :terms_of_service, acceptance: { accept: 'my_value'}
end
仮想モデル用のフォームを新規に追加します。なお仮想モデルのためのDBテーブルは不要であることに注意してください。
app/views/my_models/_form.html.erb
<%= form_for(@my_model) do |f| %>
...
<%= fields_for @my_virtual_model do |mvm_f| %> ← これを追加
<div class="field"> ←
<%= mvm_f.label :fieldA %> ←
<%= mvm_f.text_field :fieldA %> ←
</div> ←
<% end %> ←
...
<% end %>
app/models/my_virtual_model.rb (新規に作成した仮想モデル)
class MyVirtualModel
include ActiveModel::Model
attr_accessor :fieldA
validates :fieldA,
presence: true
end
app/controllers/my_models_controller.rb
class MyModelsController < ApplicationController
...
# GET /my_models/new
def new
@my_model = MyModel.new
@my_virtual_model = MyVirtualModel.new # ← これを追記
end
# GET /my_models/1/edit
def edit
@my_virtual_model = MyVirtualModel.new # ← これを追記
end
# POST /my_models
# POST /my_models.json
def create
@my_model = MyModel.new(my_model_params)
@my_virtual_model = MyVirtualModel.new(params[:my_virtual_model])
if not @my_virtual_model.valid? # ←↑ これを追記
warn 'fieldA is not set.'
end
...
end
# PATCH/PUT /my_models/1
# PATCH/PUT /my_models/1.json
def update
@my_virtual_model = MyVirtualModel.new(params[:my_virtual_model])
if not @my_virtual_model.valid? # ←↑ これを追記
warn 'fieldA is not set.'
end
...
end
...
end
そもそもフォームの項目がないなどの理由で値が nil の場合は検証せずに通します。
class MyModel < ActiveRecord::Base
validates :field1,
uniqueness: { allow_nil: true }
end
上記に加えて、空白 "\s*" である場合も検証をせずに通します。
class MyModel < ActiveRecord::Base
validates :field1,
uniqueness: { allow_blank: true }
end
これらは 'uniqueness' に限らない記法です。
新規作成 (INSERT) の場合にのみ検証
class MyModel < ActiveRecord::Base
validates :field1,
acceptance: { on: :create }
end
更新 (UPDATE) の場合にのみ検証
class MyModel < ActiveRecord::Base
validates :field1,
acceptance: { on: :update }
end
いずれの場合にも検証 (既定値です)
class MyModel < ActiveRecord::Base
validates :field1,
acceptance: { on: :save }
end
これらは 'acceptance' に限らない記法です。
if (または unless) によって検証を行う場合を指定できます。
基本形
class MyModel < ActiveRecord::Base
# field2 の値が 10 の場合のみ検証
validates :field1,
length: { is: 5, if: 'field2 == 10' }
end
基本形 その2
class MyModel < ActiveRecord::Base
# field2 が空欄の場合にのみ検証
validates :field1,
length: { is: 5, if: 'field2.blank?' }
end
関数化
class MyModel < ActiveRecord::Base
# field2 が空欄の場合にのみ検証
validates :field1,
length: { is: 5, if: 'field2_is_blank?' }
private
def field2_is_blank?
field2.blank?
end
end