箱のプログラミング日記。

渋谷の自社開発企業でRails書いてます。

中間テーブルへの保存ができないのはseedが原因かも【Rails】

f:id:y_hakoiri:20191102121842j:plain

中間テーブルへの保存ができなくて、一生懸命コードのいろんなところを探して調べたりしたけど、結局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にも気をつけよう
  • バグの確認をする時はいきなりコードから見ずにまず環境ごとの挙動の違いを確認しよう