「:on」でバリデーション発動のタイミングを指定する

f:id:endoakak:20200725103845j:plain

バリデーションが発動して欲しくないときに発動してしまって困った時の話です。

概要

deviseを使ったユーザー管理機能を実装

Userモデルにパスワードに関するバリデーションを追加

マイページでユーザー情報を編集できるように実装したい

current_user.updateで更新しようとしたらパスワードのバリデーションが発動して更新できない

問題点

ユーザーを新規登録した時、パスワードは文字列がそのままデータベースに保存されるわけではないですよね。多分暗号化されているはず。

そのためユーザー情報を更新(つまりcurrent_user.update)しようとした時、passwordの値は空になっています。

f:id:endoakak:20200815110651p:plain:h50

deviseはうまいこと対応してくれるのでしょうが、Userモデルに自分で設定したバリデーションには引っかかってしまうようです。

User.rbにはパスワードが英数字混合であるように指定していました。

PASSWORD_REGEX = /\A(?=.*?[a-zA-Z])(?=.*?[\d])[a-zA-Z\d]+\z/.freeze
validates :password, format: { with: PASSWORD_REGEX, message: "は英数字混合で入力してください" }

解決

初めは、ユーザーがログインしている時はバリデーションを発動しないように、ifを使ってなんとかしようとしました。

if !current_user.present?
  PASSWORD_REGEX = /\A(?=.*?[a-zA-Z])(?=.*?[\d])[a-zA-Z\d]+\z/.freeze
  validates :password, format: { with: PASSWORD_REGEX, message: "は英数字混合で入力してください" }
end

しかし、current_userなんてものはないと言われてしまいました。モデルの中では使えないのか。

さらに調べていたら、バリデーションが発動するタイミングを指定する方法を発見。on:で指定すればOKらしい。

パスワードのバリデーションが発動して欲しいのは新規登録の時だけで、更新の時は発動して欲しくないので、on: :createで発動のタイミングを指定しました。

PASSWORD_REGEX = /\A(?=.*?[a-zA-Z])(?=.*?[\d])[a-zA-Z\d]+\z/.freeze
validates :password, format: { with: PASSWORD_REGEX, message: "は英数字混合で入力してください", on: :create }

そうしたらいけました。無事にユーザー情報を編集して更新する機能が実装できました。こんなに簡単だったとは。

まとめ

  • バリデーション発動のタイミングを指定する時は:onオプション