Rails で多対多の中間テーブルにレコードが正しく保存されない時
問題発生フェーズ
以下のような多対多のテーブル設計がありまして
以下のように関連付けが実装されています。 (なお、一部の実装を省いています)
# group/profile.rb has_many :group_profile_users, class_name: "::Group::ProfileUser", foreign_key: :group_profile_id has_many :users, through: :group_profile_users, class_name: "::User" # group/profile_user.rb belongs_to :user, class_name: "::User" belongs_to :group_profile, class_name: "::Group::Profile" # user.rb has_many :group_profile_users, class_name: "::Group::ProfileUser", foreign_key: :user_id has_many :group_profiles, through: :group_profile_users, class_name: "::Group::Profile"
これでgroup_profile
を保存しようとするとvalidation errorで引っかかてしまい、以下のようなエラーが出ます。
(実はusersだけでなくtagsというのもあるのでした)
不正な値とだけ怒られてしまっています。そもそもまだ validation 実装してないのになぜ怒られなきゃいけないんだ…!
問題解決フェーズ
そもそもまだvalidation実装してないし、多分関連付けの部分で何か良くないことが起きてそうだなということで、belongs_to
あたりを調べてみました。belongs_to
の公式ドキュメントを読んでいてoptional
というオプションがありました。そういえば去年くらいにデフォルトがoptinal: false
に仕様変更されてた気がするなーと…しかも一年くらい前に一度同じことでハマった気がするなーと…
optional: false
になっていると、アソシエーション先でpresence: true
になってしまいます。
つまり、今回のケースだとgroup_profileとgroup_profile_usersのレコードを同時に作成しようとすると、group_profile_usersの保存の際に、まだ存在しないgroup_profileのidを持った保存とみなされてしまい、validationに引っかかってしまうということでした。
なので中間テーブルのbelongs_toに以下のように設定を追加してあげます。
# group/profile_user.rb belongs_to :user, class_name: "::User", optional: true belongs_to :group_profile, class_name: "::Group::Profile", optional: true
optional: true
を追加しただけです。無事解決しました。
去年もハマったし、あんまり検索しても出てこなかったのでまとめときました。