はじめに/エラー発生時の状況
Railsでカレンダーアプリ(旅のしおり)的なものを実装中です。
日付や詳細、メンバー(deviseのユーザー登録機能を利用)を指定し、
旅行ページを登録します。
旅行ページ(trips)の参加ユーザー(users)は、
下図のようにtrip_usersと言う中間テーブルで、多対多のアソシエーションを組んで紐付けています。
という具合に保存されます。
しかし、実装を進めていると…何かの拍子に
見たことのないエラーと共に、、旅行ページの登録が突然出来なくなりました。
※当方、エラー文を日本語化しています。
先ほどまでと同じようにメンバーを選択しているのに、**「参加メンバーは不正な値です」**とは何ぞや…
私は何をしてしまったのでしょうか…
開発環境
Ruby 2.6.5
Rails 6.0.3.4
MySQL
Visual Studio Code
(GoogleChrome)
原因の考察・検証
binding.pry
でcreate処理を確認しました。
[1] pry(#<TripsController>)> trip_params
=> <ActionController::Parameters {
(中略)
"user_ids"=>["", "2", "3", "4", "5", "6", "1"]} permitted: true>
[2] pry(#<TripsController>)> @trip = Trip.new(trip_params)
(中略)
[3] pry(#<TripsController>)> @trip.valid?
=> false
[4] pry(#<TripsController>)> @trip.errors.full_messages
=> ["参加メンバーは不正な値です"]
参加ユーザーはparamsではuser_ids
にidが格納されて送られます。
これに対して、「不正な値」と言われているようです。
さっきまでは全然不正じゃなかったのに、何が不正になったのでしょうか…
何かエラーの心あたりはないものかと、最近何をやったか、記憶をたどると…
「そういえば、パスワードの英数混合バリデーション書いてなかったな〜」
と、急に思い出して書いた記述を発見しました。
class User < ApplicationRecord
(中略)
PASSWORD_REGEX = /\A(?=.*?[a-z])(?=.*?\d)[a-z\d]+\z/i.freeze # 英数混合の正規表現
validates :password, format: { with: PASSWORD_REGEX, message: 'は英字と数字を両方含んでください' }
end
試しにコメントアウトしてみると…戻りました!
こちらが原因だった模様…
どうやら、ユーザー新規登録時のバスワードに設定した英数混合バリデーションが、アソシエーションしている他モデルの生成時にも発動してしまっていた?みたいです。
数字のみのデータであるuser_ids
に対しても、「英数混合にしてください!」と弾いていたと考えられます。
そんな珍事件もあるの…
では、そのバリデーションをユーザー登録のみに適用できないものか…
調べた結果…
こちらが使えそうです!
結果
:on
オプションでバリデーション実行のタイミングを指定することができます。
今回はユーザー登録時にのみパスワードのバリデーションを実行したいので、on: :create
を記述します。
↓修正後のUserモデル
class User < ApplicationRecord
(中略)
PASSWORD_REGEX = /\A(?=.*?[a-z])(?=.*?\d)[a-z\d]+\z/i.freeze # 英数混合の正規表現
validates :password, format: { with: PASSWORD_REGEX, message: 'は英字と数字を両方含んでください' }, on: :create
end
これでパスワードのバリデーションを残しつつ、
旅行ページの登録機能も今まで通りに戻りました!
終わりに/感想
原因となる記述を探すのに時間がかかりました。
エラー時に遡れるように、マメにコミットする重要性も学びました…
初学者で拙い記事ですが、少しでもお役に立てると嬉しく思います。
最後まで読んでいただき、誠にありがとうございました。
参考記事
【公式/Railsガイド】Active Record バリデーション
【公式】Railsドキュメント/validates
【Qiita】状況によってsave時に実行するバリデーションを切り替える
【Qiita】Rails |onオプションを使ってバリデーションのタイミングを指定