Railsのinverse_ofオプションを✅

アルコリズム(アルコール✖︎アルゴリズム)が完成して喜んでいます。自己結合最高です。

完成後、コードのチェックをするためにrubocopを走らせてみたところ、Rails/InverseOfエラーが出てしましまいました。

ということで、今回は、Rails/InverseOfエラーを解消をするために調べた内容を、アウトプットしていきたいと思います。

 

rubocopを走らせた時のエラーは下記の通り。

Image from Gyazo

inverse_ofをつけてくださいだと、、?なんですかそれは、、?

inverse_ofとは

双方向関連付け(同一のオブジェクトを参照させること)を自動で行ってくれるオプション。

rails 4.1以降デフォルトで適用されるようになりました。

has_manyhas_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モデルには外部キーのみが格納されています。

修正した結果

Image from Gyazo

Rails/InverseOf: Specify an :inverse_of option.は出なくなりました。が、controllerに規定の文字数以上の記述があると指摘されてしまっています。

あとでリファクタリングします。。

今回はここまでにしますね。

Active Record の関連付け - Railsガイド