ユーザからの入力値チェックは、一般にクライアント側とサーバ側の両方で行います。クライアント側のチェックは、例えばブラウザのJavascriptによって行うことができます。これは悪意のないユーザの操作性向上のために行います。ここでは、悪意あるユーザがJavascriptを無効にして不正なデータを送信してきた場合などに備えサーバ側でチェックを行うためのRailsのバリデーション機能の使用方法を記載します。
サンプルアプリの準備
サンプルアプリを作成します。
$ 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 が返されます。
- @my_model.save
- @my_model.update(my_model_params)
- @my_model.valid?
バリデーションに失敗した場合はそのエラー内容がモデルインスタンスに格納されます。以下のようにしてビューで参照できます。
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>
Railsに用意されている基本的なバリデーションパターン
何らかの値が入力されたことを検証
基本形
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
- \A : 文字列の先頭に合致 (改行直後は除く)
- \z : 文字列の末尾に合致 (改行が続く場合は除く)
なお '\z' であって '\Z' ではないので注意しましょう。
pattern123
← ここ、改行されるだけで文字列を含まない (「\Apattern\d+\Z」は合致する)
- \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 文で指定
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
独自の検証パターンを追加する
モデルに特化した検証パターン
あるモデルに特有の検証パターンであれば、汎用性は低くなりますが直接モデルファイルに記述すればよいです。
app/models/my_model.rb
class MyModel < ActiveRecord::Base
validate :my_1_valid?, :my_2_valid?
private
def my_1_valid?
errors.add(:field1, 'is invalid') if field1 == 'my_value1'
end
def my_2_valid?
errors.add(:field1, 'is invalid') if field1 == 'my_value2'
end
end
モデルに依存しない検証パターン
汎用的な検証パターンを作成するためには、専用の検証クラスを新規で作成します。
オプション指定が不要で、他のフィールド値も不要な検証
app/models/my_model.rb
class MyModel < ActiveRecord::Base
validates :field1,
sample: true
end
app/models/sample_validator.rb (新規作成したファイル)
class SampleValidator < ActiveModel::EachValidator
def validate_each(record, attribute, value)
record.errors.add(attribute, 'is invalid') if not value == 'my_value'
end
end
オプション指定が可能な検証
app/models/my_model.rb
class MyModel < ActiveRecord::Base
validates :field1,
sample2: { my_option: true }
# sample2: true # ← オプションを指定しない場合はこうします
end
app/models/sample2_validator.rb
class Sample2Validator < ActiveModel::EachValidator
def validate_each(record, attribute, value)
if options[:my_option] # オプション指定がなされた場合
my_value = 'hoge1'
else # オプション指定がなされなかった場合
my_value = 'hoge2'
end
record.errors.add(attribute, 'is invalid') if not value == my_value
end
end
他のフィールド値が必要な検証
app/models/my_model.rb
class MyModel < ActiveRecord::Base
validates :field1,
sample3: { other_field: 'field2' }
end
app/models/sample3_validator.rb
class Sample3Validator < ActiveModel::EachValidator
def validate_each(record, attribute, value)
value_of_other_field = record.attributes[options[:other_field]]
record.errors.add(attribute, 'is invalid') if not value_of_other_field == 123
end
end
関連記事
- レイアウトおよび部分テンプレートに関するまとめ (Rails4)レイアウトおよび部分テンプレートはどちらもテンプレート (*.html.erb) に共通の要素をまとめておき、任意のテンプレートから利用できるようにしておくための仕組みです。フッターやヘッダーといった大枠はレイアウト、小さなパーツは部分テンプレートというイメージで使い分けましょう。 レイアウトの使用方法 クラス毎に指定する方法と、アクション毎に指定する方法があります。 app/views/layo...
- Ruby コードスニペット (正規表現)sample.rb str = "001: This is a string." var1,var2 = 2,3 # 'EOS'とすると#{}による変数展開がなされない (%03dは展開される) doc = (<<"EOS" % var1) # 括弧は省略可。要は<<"EOS"の次の行からEOSまで。(参: <<-"EOS"とすると前に空白...
- OAuthを用いずにTwitterに自動投稿する (回数制限あり, Selenium with Ruby)Seleniumを用いて、OAuthを用いずにTwitterに自動投稿するRubyスクリプトを記述してみます。連続で複数回実行すると、ボット判定としてキャプチャ認証が発生します。その認証までは通過できませんので悪しからず。また、Twitterの仕様変更次第ではDOMの構造が変化するため、下記サンプルは機能しなくなる恐れが有ります。 twitter_post.rb #!/usr/bin/ruby r...
- Ruby における日本語のエンコーディング日本語を含めて多言語対応する際には、Asciiコード以外の文字コードセットが必要になります。日本語が主となる場合、よく使われる文字セットにはUnicode, Shift_JIS, EUC-JPがあります。このうち Unicode だけは特殊であり、世界中のあらゆる文字を収録しようとしていることから 1 文字を表現するために必要なバイト数が大きくなってしまっています。そのため Unicode のうち...
- Rails3ビューテンプレートの基本的な使用方法 (Ruby)Railsでは、ERB (eRuby (テキストファイルにRubyスクリプトを埋込む書式の仕様) をRubyで実装したもの) を用いてHTML内にRubyスクリプトを埋込むことができます。 <% %> で囲むと出力されません (if-elseなど制御構文を記述します) <%= %> で囲むとエスケープ出力されます <%== %> で囲むとエスケープされずに...