inverse_of カスタマイズされた関連の双方向関連を指定する
使用可能な関連付け
belongs_to
has_many
has_one
対となる関連に指定するオプション
なし
概要
厳密な規則性を把握していないので厳密なパターンはここでは省略するが、ActiveRecordの関連が双方向関連になっている場合、すでにインスタンスを作っている場合は同じインスタンスが使われるケースがある。
例えば以下のような定義がある場合、
class Profile < ApplicationRecord
has_one :author
end
class Author < ApplicationRecord
belongs_to :profile
has_many :authorships
end
class Authorship < ApplicationRecord
belongs_to :author
end
以下のように関連を辿ってProfileの関連に至ったとしても同じインスタンスを返すようになっている。
profile = Profile.last
profile.object_id == profile.author.authorships.last.author.profile.object_id
# => true
これを以下のように変える。テーブルはそのまま、Author
をAuthorx
に変更している。それに伴い、class_name
やforeign_key
の設定も追加している。
class Profile < ApplicationRecord
# has_one :author
has_one :author, class_name: 'Authorx'
end
class Authorx < ApplicationRecord
self.table_name = :authors
belongs_to :profile
# has_many :authorships
has_many :authorships, foreign_key: :author_id
end
class Authorship < ApplicationRecord
# belongs_to :author
belongs_to :author, class_name: 'Authorx'
end
このように通常の関連定義から逸脱した状態で、同じように関連を辿ると別のインスタンスを返すようになってしまう。
profile = Profile.last
profile.object_id == profile.author.authorships.last.author.profile.object_id
# => false
このような場合でも、通常の関連定義と同じように同じインスタンスを返すようにさせるのがinverse_of
オプションである。
Authorx
のauthorships
関連にオプションを追加する。
has_many :authorships, foreign_key: :author_id, inverse_of: :author
こうすると、同じインスタンスを返すようになる。
profile = Profile.last
profile.object_id == profile.author.authorships.last.author.profile.object_id
# => true
どの関連にinverse_of
をつける必要があるか
考えるよりも、rubocop-railsというRuboCopのextension gemにRails/InverseOfというCopが用意されているので、それに任せれば良い。
inverse_of
の無効化
inverse_of: false
を指定することで無効化できる。Rails/InverseOfの説明を読む限りでは、inverse_of: nil
では自動的な双方向関連解決の実行を止められないようである。コードを読んでみないとわからないが、inverse_of: nil
を試してみた感じではinverse_of: false
と違いがないように見えるので、内部的な処理が違っていてnilだと解決を試みて失敗しているとかなのかもしれない。