【バリデーション】numericality
はじめに
こんにちは!大ちゃんの駆け出し技術ブログです。
本記事ではnumericality
について説明します。
これは数値に対して使われるバリデーションです。例えば、整数しか入力できないとか、〇〇以上〇〇未満しか入力できない、といった制約をつけることができます。
特に難しいということはないので、早速やってみましょう。
また、今回は数値型のバリデーションがしっかりと反映されているかを確認するために、RSpecを使用しモデルスペックを作成します。
テキトーにカラムを追加
アプリケーション自体今回は関係ないのでテキトーなモデルに対し、数値型カラムを追加してくださいww
カラムの追加の方法は下記のとおりです。
$ rails g migration Addカラム名Toテーブル名 カラム名:型
私は既存のUserモデルに対して、test_numericality
カラムをinteger型で追加します。
$ rails g migration AddTestNumericalityToUsers test_numericality:integer invoke active_record create db/migrate/20210217081751_add_test_numericality_to_users.rb
マイグレーションファイルを念のため確認。
class AddTestNumericalityToUsers < ActiveRecord::Migration[6.0] def change add_column :users, :test_numericality, :integer end end
問題なさそうですね。DBレベルに反映しましょう。
$ rails db:migrate == 20210217081751 AddTestNumericalityToUsers: migrating ======================= -- add_column(:users, :test_numericality, :integer) -> 0.0024s == 20210217081751 AddTestNumericalityToUsers: migrated (0.0026s) ==============
モデルスペックの作成
モデルスペックを作成します。Rspecはインストール済みであるという認識で進めていきます。
モデルスペックファイルの作成
$ rails generate rspec:model user create spec/models/user_spec.rb invoke factory_bot create spec/factories/users.rb
FactoryBotにはtest_numericality
にとりあえず10を入れておきましょう。なお、name
カラムに関しては自分のUserモデルにあるカラムなので注意してくださいね!
# spec/factories/users.rb FactoryBot.define do factory :user do name { 'test_name' } test_numericality { 10 } end end
あと、FactoryBot作成時に省略記法を使用したいので、rails_helper.rb
に下記内容を記載します。
# spec/rails_helper.rb config.include FactoryBot::Syntax::Methods
モデルスペックファイルに下記内容を記載
# spec/models/user_spec.rb RSpec.describe User, type: :model do describe "バリデーションのテスト" do it '全てのカラムが正常値の時、Userモデルはvalidである' do user = build(:user) expect(user).to be_valid expect(user.errors).to be_empty end end end
これでテストを実行すると当然通ります。バリデーションがないためです。
$ bundle exec rspec spec/models/user_spec.rb . Finished in 0.02555 seconds (files took 1.75 seconds to load) 1 example, 0 failures
numericalityを試す
大変お待たせいたしました笑。
指定のモデルに対してnumericality
を使用してバリデーションをかけてみましょう。
numericality
は基本的に以下のように記述します。
validates :カラム名, numericality: { オプション: オプションに対応した値 }
では最初に簡単なものから紹介します。
only_integer
これは値を整数のみ入力できるようにするバリデーションです。
# app/models/user.rb validates :test_numericality, numericality: { only_integer: true }
現状はFactoryBotにデフォルトで10が格納されているためテストは通るはずです。この値を小数点ありの10.5にしてみましょう。
RSpec.describe User, type: :model do describe "バリデーションのテスト" do it '全てのカラムが正常値の時、Userモデルはvalidである' do user = build(:user, test_numericality: 10.5) expect(user).to be_valid expect(user.errors).to be_empty end end end
これでテストを実行すると、
$ bundle exec rspec spec/models/user_spec.rb F Failures: 1) User バリデーションのテスト 全てのカラムが正常値の時、Userモデルはvalidである Failure/Error: expect(user).to be_valid expected #<User id: nil, name: "test_name", created_at: nil, updated_at: nil, test_numericality: 10> to be valid , but got errors: Test numericality must be an integer # ./spec/models/user_spec.rb:8:in `block (3 levels) in <top (required)>' Finished in 0.03144 seconds (files took 1.03 seconds to load) 1 example, 1 failure
エラー内容の一部に、「Test numericality must be an integer
」とあります。
user.errors[:test_numericality]
=> ["must be an integer"]
次のオプションに移る前に作成するFactoryBotを元の状態に戻しておいてください。
RSpec.describe User, type: :model do describe "バリデーションのテスト" do it '全てのカラムが正常値の時、Userモデルはvalidである' do user = build(:user) expect(user).to be_invalid # binding.irb expect(user.errors).to be_empty end end end
greater_than、greater_than_or_equal_to
次は「〜より大きい」、「〜より以上」を表すバリデーションです。「〜より大きい」はgreater_than
です。さきほどのオプションであるonly_integer
ではboolean
型であるtrue
を入れましたが、今回は数値型を入れます。数値は10を入れてみましょう。
# app/models/user.rb validates :test_numericality, numericality: { greater_than: 10 }
これでテストを実行すると失敗します。
$ bundle exec rspec spec/models/user_spec.rb F Failures: 1) User バリデーションのテスト 全てのカラムが正常値の時、Userモデルはvalidである Failure/Error: expect(user).to be_valid expected #<User id: nil, name: "test_name", created_at: nil, updated_at: nil, test_numericality: 10> to be valid , but got errors: Test numericality must be greater than 10 # ./spec/models/user_spec.rb:7:in `block (3 levels) in <top (required)>' Finished in 0.02056 seconds (files took 0.95972 seconds to load) 1 example, 1 failure
エラー内部には以下の文字列がありますね。
"Test numericality must be greater than 10"
「10より大きい数」と制約しているので、10は含まれないということですね。10を含ませるためには「10以上の数」と制約をかけます。greater_than_or_equal_to
を使用すると「〜以上の数」という制約をかけることができます。
# app/models/user.rb validates :test_numericality, numericality: { greater_than_or_equal_to: 10 }
これでテストが通ります。
$ bundle exec rspec spec/models/user_spec.rb . Finished in 0.02247 seconds (files took 1.64 seconds to load) 1 example, 0 failures
「〜より大きい」、「〜より以上」の反対として「〜より小さい」、「〜より以下」もあります。less_than
とless_than_or_equal_to
です。この2つについては皆さんの手で試してみてください。
equal_to, other_than
指定した値以外が格納されるとエラーが出るようにしたい場合はequal_to
です。
validates :test_numericality, numericality: { equal_to: 100 }
これでは現在のテストは当然通りません。
$ bundle exec rspec spec/models/user_spec.rb F Failures: 1) User バリデーションのテスト 全てのカラムが正常値の時、Userモデルはvalidである Failure/Error: expect(user).to be_valid expected #<User id: nil, name: "test_name", created_at: nil, updated_at: nil, test_numericality: 10> to be valid, but got errors: Test numericality must be equal to 100 # ./spec/models/user_spec.rb:7:in `block (3 levels) in <top (required)>' Finished in 0.05692 seconds (files took 1.78 seconds to load) 1 example, 1 failure
「Test numericality must be equal to 100
」とあるように値が必ず100でなければなりません。
反対に指定の数値だけ絶対に格納されてはいけないという制約もあります。other_than
を使います。
validates :test_numericality, numericality: { other_than: 100 }
テストが通りました。
$ bundle exec rspec spec/models/user_spec.rb . Finished in 0.02088 seconds (files took 1.72 seconds to load) 1 example, 0 failures
odd、even
odd
は奇数、even
は偶数を制約します。only_integer
と同様にboolean型で指定します。
odd
をtrue
としておけば、
validates :test_numericality, numericality: { odd: true }
テストは通りませんが、
$ bundle exec rspec spec/models/user_spec.rb F Failures: 1) User バリデーションのテスト 全てのカラムが正常値の時、Userモデルはvalidである Failure/Error: expect(user).to be_valid expected #<User id: nil, name: "test_name", created_at: nil, updated_at: nil, test_numericality: 10> to be valid, but got errors: Test numericality must be odd # ./spec/models/user_spec.rb:7:in `block (3 levels) in <top (required)>' Finished in 0.04629 seconds (files took 1.79 seconds to load) 1 example, 1 failure
evenをtrue
にすると、
validates :test_numericality, numericality: { even: true }
テストはパスしました。
$ bundle exec rspec spec/models/user_spec.rb . Finished in 0.02802 seconds (files took 1.8 seconds to load) 1 example, 0 failure
終わりに
本記事では雑にカラムを追加しているので具体的な用途がわからない方もいるのかもしれませんが、数値の制約をすることで、入れたい値、入れたくない値を指定できるので安心できます。
格納されるべき値を簡単に制限できるので、使うタイミングがあればすぐに導入できるので本当に便利で使い勝手が良いと思います。
以上、大ちゃんの駆け出し技術ブログでした!