中間テーブルへの保存ができなくて、一生懸命コードのいろんなところを探して調べたりしたけど、結局DBが原因でしたよという話。
まずモデルの説明から
今回はuserモデルとgroupモデルがあり、中間テーブルはgroup_user。
あるグループを作成する時に複数userを選択しますが、 grouopsテーブルにはグループ名やidなどが入り、 所属するメンバーを参照するためには中間テーブルを経由します。
コード確認&原因調査
group.rb
class Group < ApplicationRecord has_many :group_users, dependent: :delete_all has_many :users, through: :group_users end
user.rb
class User < ApplicationRecord has_many :group_users, dependent: :delete_all has_many :groups, through: :group_users end
group_user.rb
class GroupUsers < ApplicationRecord belongs_to :group belongs_to :user end
ちなみに、コードは省略しますがviewのフォームではuser_idsを配列で渡していて、 ストロングパラメーターでもpermitされている。
コントローラーで以下のように、save!にして見た所、
if @user_group.save! redirect_to root_path, notice: 'グループを作成しました' else render :new end
「バリデーションに失敗しました Usersは不正な値です」と出ました。(キャプチャ撮り忘れた)
中間テーブルへの保存って、特にviewやモデルで特別な記述をする訳ではなく、 アソシエーションが正しく組まれていて、groupをcreateするフォームでuser_idを配列で複数渡せていたら問題ないという認識だったけど、違ったかな。。
そもそもなぜここでUsersにバリデーションがかかる...?
環境ごとの違いがないか確認
ここで、ローカルではなくステージングや本番環境でも同じような挙動が起こっているか確認してみようと思いつく。(最初からこれをやればよかった)
すると、なんとコードは同じなのに(ローカルはさっき最新をpullして来たばっかり)ステージングや本番環境では中間テーブルへの保存ができてるじゃないか。。なんてこと。
DBをリセット
コードが同じなのに環境ごとに違うことといえば、DBぐらいかな?
と思ってローカルのDBをリセットして
bin/rake db:migrate:reset
seedを入れ直してみた
bin/rake db:seed_fu
けど、変わらず。
ここで先輩に聞く
自分的に、「ローカルでは動いたのにデプロイしてみたらうまくいかなかった」ってことは多々経験があるのですが、 逆のパターンは初めてだったので「そもそもそんなことってあるのか?」と思って先輩に聞いてみた。
すると、たまにあるらしい。
そしてだいたいDB、というかseedがおかしいらしい。
えっ、そこか。。(盲点)
原因発覚
原因はUserのseed値がおかしかったことでした。
解説
後から仕様変更になったりカラムを追加したりして、presense: :trueのバリデーションが追加されたにも関わらず、seedでその値が追加されていないと思わぬところでバリデーションエラーが発生する。
つまり、初期のDBに保存されているuserではなく新しく作ったuserを使ってgroupを作成すると、ローカルでも成功した。
今回はusersに対して、 住所や郵便番号にpresense: :trueのバリデーションがかけられているにも関わらず、
seedで作られたレコードにはそれらが入っていなかったので、 そのuserレコードを使用してgroupを作成しようとした時に「このuserは不正だ!」っていうバリデーションエラーが発生したらしい。。
なんか、そんなところでも検証してくれるのか。
解決策
上記の通りなので、user.rbでバリデーションがかかっているものを参照しながら、seedファイルに不足していた値の記述を追加。
seedをやり直して、埋まるべきカラムが全て埋まった状態のuserが出来上がった状態でgroupを作成したら、うまくいった。
今回の学び
- 後からカラムやバリデーションを追加した時はseedにも気をつけよう
- バグの確認をする時はいきなりコードから見ずにまず環境ごとの挙動の違いを確認しよう