Railsのinverse_ofオプションを✅
アルコリズム(アルコール✖︎アルゴリズム)が完成して喜んでいます。自己結合最高です。
完成後、コードのチェックをするためにrubocopを走らせてみたところ、Rails/InverseOfエラーが出てしましまいました。
ということで、今回は、Rails/InverseOfエラーを解消をするために調べた内容を、アウトプットしていきたいと思います。
rubocopを走らせた時のエラーは下記の通り。
inverse_ofをつけてくださいだと、、?なんですかそれは、、?
inverse_ofとは
双方向関連付け(同一のオブジェクトを参照させること)を自動で行ってくれるオプション。
rails 4.1以降デフォルトで適用されるようになりました。
has_many
, has_one
や belongs_to
のような双方向関連付けのメソッドに関してはrails側がよしなに設定してくれる一方、自分で定義した関連付けには明示的にオプションを付ける必要があるそうです。
そんな時にinverse_ofオプションを使用します。
ちなみに双方向関連付けとは
2つのモデル両方に関連を定義すること。belongs_toやhas_manyがよく使われる。
class Drinker < ApplicationRecord
has_many :alcohols
end
class Alcohol < ApplicationRecord
belongs_to :drinker
end
Active Recordでスコープや次のオプションを使った場合、双方向の関連付けは自動的に認識されないようです。
:through
:foreign_key
Active Record の関連付け - Railsガイド
試しに次のような双方向関連付けがなされているモデルを見てみます。
class Drinker < ApplicationRecord
has_many :alcohols
end
class Alcohol < ApplicationRecord
belongs_to :heavy_drunker, class_name: 'Drinker', foreign_key: 'drinker_id'
end
この場合、Active Recordは双方向の関連付けを自動的に認識する事ができません。
a = Drinker.first
b = a.alcohols.first
a.first_name == b.drinker.first_name # => true
a.first_name = 'Subaru'
a.first_name == b.writer.first_name # => false
そこで、Active Recordが提供する:inverse_of
オプションが登場します。
このオプションを使うと、双方向の関連付けを明示的に宣言できます。
class Drinker < ApplicationRecord
has_many :alcohols, inverse_of: :heavy_drunker
end
class Alcohol < ApplicationRecord
belongs_to :heavy_drunker, class_name: 'Drinker', foreign_key: 'drinker_id'
end
has_many
の関連付けを宣言するときに:inverse_of
オプションも含めることで、Active Recordは双方向の関連付けを認識するようになりました。
a = Drinker.first
b = a.alcohols.first
a.first_name == b.drinker.first_name # => true
a.first_name = 'Subaru'
a.first_name == b.writer.first_name # => true
Rails model with rubocop error - specify an :inverse_of
option
エラー修正
rubocopを走らせて出現したエラーを修正していきます。
でたエラーは以下の通り。
C: Rails/InverseOf: Specify an :inverse_of option.
has_many :liquor_relationships, class_name: 'Relationship', foreign_key: 'liquor_box_id', dependent: :destroy
^^^^^^^^
C: Rails/InverseOf: Specify an :inverse_of option.
has_many :liquor_box_relationships, class_name: 'Relationship', foreign_key: 'liquor_id', dependent: :destroy
該当箇所にinverse_ofを振っていきます。
class Alcohol < ApplicationRecord
# 1000ボックスに属するliquor_idを取得するassociation
has_many :liquor_relationships,
class_name: 'Relationship',
foreign_key: 'liquor_box_id',
dependent: :destroy,
inverse_of: :liquor #⇦ここを追加
# コーヒーが属するliquor_box_idを取得するassociation
has_many :liquor_box_relationships,
class_name: 'Relationship',
foreign_key: 'liquor_id',
dependent: :destroy,
inverse_of: :liquor_box #⇦ここを追加
end
has_manyを記述しているモデル内に記述するようです。
Alcoholモデルと関連づけられたRelationshipモデルは以下の通り。
class Relationship < ApplicationRecord
belongs_to :liquor, class_name: 'Alcohol'
belongs_to :liquor_box, class_name: 'Alcohol'
end
Relationshipモデルは、Alcoholモデルで自己結合をした際に作られた中間テーブルになります。
Userのフォロー、アンフォローの時に実装したN対Mの関連付けでお世話になったあの自己結合です。ちなみにRelationshipモデルには外部キーのみが格納されています。
修正した結果
Rails/InverseOf: Specify an :inverse_of option.は出なくなりました。が、controllerに規定の文字数以上の記述があると指摘されてしまっています。
あとでリファクタリングします。。
今回はここまでにしますね。