サーバ側で行うフォーム入力値のバリデーション (Rails4)
[最終更新] (2019/06/03 00:43:52)
最近の投稿
注目の記事

概要

ユーザからの入力値チェックは、一般にクライアント側とサーバ側の両方で行います。クライアント側のチェックは、例えばブラウザの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 が返されます。

  • @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

独自の検証パターンを追加する

この続きが気になる方は

サーバ側で行うフォーム入力値のバリデーション (Rails4)

残り文字数は全体の約 14 %
tybot
100 円
関連ページ
    概要 Spring Boot のテンプレートエンジンとしては、こちらで使用方法を把握した Thymeleaf が有名です。本ページでは、フォーム関連の処理について、基本的なサンプルコードをまとめます。Rails におけるビューヘルパーや、フォーム入力値のバリデーションに相当する機能です。 公式ドキュメント Handling Form Submission