【Rails】バリデーションをまとめてみる
データを登録する際に利用するフォームですが、条件に応じて必須だったり制限したりする仕組みを「バリデーション」といいます。 Ruby on Railsでは以下のような感じで設定することができます。
class Hoge < ApplicationRecord validates :name, presence: true end
# validationがない場合 > hoge = Hoge.new => #<Hoge:0x00007f803eabca78 id: nil, name: "", age: 0, gender: "man", birthday: Mon, 01 Jan 1900, created_at: nil, updated_at: nil> > hoge.save (0.3ms) BEGIN Hoge Create (2.8ms) INSERT INTO "hoges" ("created_at", "updated_at") VALUES ($1, $2) RETURNING "id" [["created_at", "2019-09-24 11:19:16.757343"], ["updated_at", "2019-09-24 11:19:16.757343"]] (0.8ms) COMMIT => true # validationがある場合 > hoge = Hoge.new => #<Hoge:0x00007fc554e8fd48 id: nil, name: "", age: 0, gender: "man", birthday: Mon, 01 Jan 1900, created_at: nil, updated_at: nil> [2] pry(main)> hoge.save (0.3ms) BEGIN (0.3ms) ROLLBACK => false [3] pry(main)> hoge.errors.full_messages => ["Name入力してください。"]
このようにvalidationを設定することで、条件が満たされていないとデータが保存されないように出来ます。
今回は入力が必須の設定であるpresence
を使ったので、最後のエラー文に"Name入力してください。"
といったエラーが自動的に設定されます。
これらの設定が色々とありますので、そちらをまとめていきたいと思います!
今回使うテーブルはこちら↓↓
Table "public.hoges" Column | Type | Collation | Nullable | Default -------------+-----------------------------+-----------+----------+----------------------------------- id | bigint | | not null | nextval('hoges_id_seq'::regclass) name | character varying | | not null | ''::character varying age | integer | | not null | 0 gender | character varying | | not null | 'man'::character varying birthday | date | | not null | '1900-01-01'::date email | character varying | | not null | postal_code | character varying | | not null | created_at | timestamp without time zone | | not null | updated_at | timestamp without time zone | | not null | Indexes: "hoges_pkey" PRIMARY KEY, btree (id)
バリデーションが実行されるタイミング
バリデーションが実行されるタイミングは色々とあるのですが、例えばこの二つです!
hoge.save
hoge.valid?
save
はその名の通りデータを保存する際に使い、valid?
は保存せずにvalidationが成功するかどうかを判断する時に使います。
また、save(validate: false)
とするとvalidationしないようにすることも可能です。
他のものは、また別の機会でまとめてみたいと思います。
バリデーションのエラー
バリデーションに失敗すると勝手にエラーに関する情報が保存されます。
> hoge.errors => #<ActiveModel::Errors:0x00007fe8e626a940 @base=#<Hoge:0x00007fe8ca962638 id: nil, name: "ai", age: 0, gender: "man", birthday: Mon, 01 Jan 1900, created_at: nil, updated_at: nil>, @details={:name=>[{:error=>:too_short, :count=>10}]}, @messages={:name=>["は10文字以上で入力してください。"]}> > hoge.errors.full_messages => ["Nameは10文字以上で入力してください。"]
errors
とすると、モデルの詳細な情報が取得できて、errors.full_messages
とするとエラーメッセージだけ取得できます。
使われ方としては、このfull_messages
を画面側で表示させてエラー内容を表示させたりします。
ここでName
という形で英語で表示されてしまってますが、これもi18n
という仕組みを使うことで日本語表示させることも可能です。
が、ここでは説明しません。別の機会に使い方をまとめたいと思います。
バリデーションの種類
空かどうか(presence)
validates :name, presence: true > hoge = Hoge.new => #<Hoge:0x00007fc556fe6618 id: nil, name: "", age: 0, gender: "man", birthday: Mon, 01 Jan 1900, created_at: nil, updated_at: nil> > hoge.valid? => false
文字数(length)
# 最小の数 validates :name, length: { minimum: 10 } # 最大の数 validates :name, length: { maximum: 20 } # 数の範囲 validates :name, length: { in: 10..20 } > hoge.name = 'ai' => "ai" > hoge.name.length => 2 > hoge.valid? hoge=> false > hoge.errors.full_messages => ["Nameは10文字以上で入力してください。" > hoge.name = 'aiueokakikukekosashisuseso' => "aiueokakikukekosashisuseso" > hoge.name.length => 26 > hoge.valid? => false > hoge.errors.full_messages => ["Nameは20文字以内で入力してください。"]
ユニーク(uniqueness)
# name単体でユニーク validates :name, uniqueness: true # name/emailでユニーク validates :name, uniqueness: { scope: [:email] } > Hoge.all Hoge Load (0.5ms) SELECT "hoges".* FROM "hoges" => [#<Hoge:0x00007fe60d860628 id: 1, name: "", age: 0, gender: "man", birthday: Mon, 01 Jan 1900, created_at: Tue, 24 Sep 2019 20:19:16 JST +09:00, updated_at: Tue, 24 Sep 2019 20:19:16 JST +09:00>, #<Hoge:0x00007fe60d831c38 id: 2, name: "opiyo", age: 30, gender: "man", birthday: Mon, 01 Jan 1900, created_at: Tue, 24 Sep 2019 20:58:20 JST +09:00, updated_at: Tue, 24 Sep 2019 20:58:20 JST +09:00>] > hogehoge = Hoge.new => #<Hoge:0x00007fe604662938 id: nil, name: "", age: 0, gender: "man", birthday: Mon, 01 Jan 1900, created_at: nil, updated_at: nil> > hogehoge.name = 'opiyo' => "opiyo" > hogehoge.age = 20 => 20 > hogehoge.save (0.3ms) BEGIN Hoge Exists (0.4ms) SELECT 1 AS one FROM "hoges" WHERE "hoges"."name" = $1 LIMIT $2 [["name", "opiyo"], ["LIMIT", 1]] (0.3ms) ROLLBACK => false > hogehoge.errors.full_messages => ["Nameはすでに存在します。"]
数値のみ(numericality)
validates :age, numericality: true > hoge.age = 'opiyo' => "opiyo" > hoge.valid? => false > hoge.errors.full_messages => ["Ageは数値で入力してください。"]
正規表現(format)
validates :postal_code, format: { with: /\A[0-9]{3}-[0-9]{4}\z/ } > hoge.postal_code = '1234-567' => "1234-567" > hoge.valid? => false > hoge.errors.full_messages => ["Postal codeは不正な値です。"]
条件付き(if:、on:)
# カラムの値やメソッドなどで条件を設定する時 validates :name, presence: true, if: Proc.new { age < 20 } # アクションに応じて設定する時 validates :postal_code, format: { with: /\A[0-9]{3}-[0-9]{4}\z/ }, on: update > hoge.age = 18 => 18 > hoge.save (0.3ms) BEGIN (0.3ms) ROLLBACK => false > hoge.errors.full_messages => ["Name入力してください。"] > hoge.age = 30 => 30 > hoge.valid? => true
バリデーションを自分で作る(validate、with_options)
# メソッドを作ってチェックする validate :enter_name_if_minor def enter_name_if_minor if self.age < 20 errors[:base] << '未成年の場合は名前を入力してください!' end end > hoge.age = 18 => 18 [3] pry(main)> hoge.valid? => false [4] pry(main)> hoge.errors.full_messages => ["Age未成年の場合は名前を入力してください!"] # 別のバリデーションを作る(メール送る処理の時だけ使うバリデーション) with_options on: :send_mail do validates :email, presence: true end > hoge.valid?(:send_mail) => false > hoge.errors.full_messages => ["Email入力してください。"] > hoge.valid? => true # saveを使うときは`context`を使う > hoge.save(context: :send_mail)
その他
その他にも複雑なvalidationが必要な場合に色々なgemがあったりします。
- phonelib(電話番号のvalidation)
- email_validator(メールアドレスのvalidation)
上記で紹介した正規表現(:format)を使えば実現できるのですが、電話番号やメールアドレスは結構考えることが多く大変です。 こういう場合は、だいたいgemがあるのでそれを使えばokだと思います!
Rails最高ですね。