Ruby
ActiveRecord
Rails5

Rails5の自己結合でハマる

tl;dr

Rails5を利用している環境で、1対多の自己結合を実現させるために以下のようなコードを書いた。

class Hoge < ApplicationRecord
  has_many :children, class_name: 'Hoge', foreign_key: 'parent_id', dependent: :destroy
  belongs_to :parent, class_name: 'Hoge'
end

何度DBにレコードを登録しようとしても、毎回以下のようなエラーが発生して登録できない。

ActiveRecord::RecordInvalid: バリデーションに失敗しました: Parentを入力してください

いや、親に当たるレコードは、parent_id持たないんだよなぁ。。。ってことで盛大にハマったのでメモ。

原因

参考にしたサイトの情報が古い(Rails4以前のもの)ばかりだったので、Rails5からの仕様変更でハマっていた。

Rails5からはbelongs_toアソシエーションは、デフォルトでrequire: trueになったので、nullでの登録ができなくなっていた。
※確かに、belongs_toなのに、id設定しないとかレアパターンなので、デフォルトをそっちにするのは自然なんだけど。

逆に、これまで通り(Rails4みたいに)belongs_toのアソシエーションでnullを許容したい場合は、optional: true使えばエラーは発生しなくなる。

class Hoge < ApplicationRecord
  has_many :children, class_name: 'Hoge', foreign_key: 'parent_id', dependent: :destroy
  belongs_to :parent, class_name: 'Hoge', optional: true
end