モーダルを閉じる工作HardwareHub ロゴ画像

工作HardwareHubは、ロボット工作や電子工作に関する情報やモノが行き交うコミュニティサイトです。さらに詳しく

利用規約プライバシーポリシー に同意したうえでログインしてください。

目次目次を開く/閉じる

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

モーダルを閉じる

ステッカーを選択してください

お支払い手続きへ
モーダルを閉じる

お支払い内容をご確認ください

購入商品
」ステッカーの表示権
メッセージ
料金
(税込)
決済方法
GooglePayマーク
決済プラットフォーム
確認事項

利用規約をご確認のうえお支払いください

※カード情報はGoogleアカウント内に保存されます。本サイトやStripeには保存されません

※記事の執筆者は購入者のユーザー名を知ることができます

※購入後のキャンセルはできません

作成日作成日
2014/11/16
最終更新最終更新
2021/09/07
記事区分記事区分
一般公開

目次

    パフォーマンス重視のRust開発者。WebAssemblyにも挑戦中。

    ユーザからの入力値チェックは、一般にクライアント側とサーバ側の両方で行います。クライアント側のチェックは、例えばブラウザの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
    
    Likeボタン(off)0
    詳細設定を開く/閉じる
    アカウント プロフィール画像

    パフォーマンス重視のRust開発者。WebAssemblyにも挑戦中。

    記事の執筆者にステッカーを贈る

    有益な情報に対するお礼として、またはコメント欄における質問への返答に対するお礼として、 記事の読者は、執筆者に有料のステッカーを贈ることができます。

    >>さらに詳しくステッカーを贈る
    ステッカーを贈る コンセプト画像

    Feedbacks

    Feedbacks コンセプト画像

      ログインするとコメントを投稿できます。

      ログインする

      関連記事

      • レイアウトおよび部分テンプレートに関するまとめ (Rails4)
        レイアウトおよび部分テンプレートはどちらもテンプレート (*.html.erb) に共通の要素をまとめておき、任意のテンプレートから利用できるようにしておくための仕組みです。フッターやヘッダーといった大枠はレイアウト、小さなパーツは部分テンプレートというイメージで使い分けましょう。 レイアウトの使用方法 クラス毎に指定する方法と、アクション毎に指定する方法があります。 app/views/layo...
        taro三世taro三世9/21/2016に更新
        いいねアイコン画像0
      • Ruby コードスニペット (正規表現)
        sample.rb str = "001: This is a string." var1,var2 = 2,3 # 'EOS'とすると#{}による変数展開がなされない (%03dは展開される) doc = (<<"EOS" % var1) # 括弧は省略可。要は<<"EOS"の次の行からEOSまで。(参: <<-"EOS"とすると前に空白...
        だいふくうさぎだいふくうさぎ4/13/2018に更新
        いいねアイコン画像0
      • 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 のうち...
        だいふくうさぎだいふくうさぎ3/21/2017に更新
        いいねアイコン画像0
      • Rails3ビューテンプレートの基本的な使用方法 (Ruby)
        Railsでは、ERB (eRuby (テキストファイルにRubyスクリプトを埋込む書式の仕様) をRubyで実装したもの) を用いてHTML内にRubyスクリプトを埋込むことができます。 <% %> で囲むと出力されません (if-elseなど制御構文を記述します) <%= %> で囲むとエスケープ出力されます <%== %> で囲むとエスケープされずに...