LoginSignup
7
2

More than 1 year has passed since last update.

RuboCopの Rails/InverseOf に引っかかった

Posted at

Rails/InverseOfは何の警告か?

アソシエーションを定義する際に明示的に :inverse_of を指定すべき箇所で指定されていないことを警告している。

:inverse_ofとは何者か?

Railsガイドより。
ActiveRecordで、双方向の関連付けを明示的に宣言するもの。

class User
  has_many :posts
end

class Post
  belongs_to :user
end

inverse_ofが効いていない場合、下例のようになる。

user = User.first
post = user.posts.first
user.name == post.user.name #=> true
user.name = "hogehoge"
user.name == post.user.name #=> false

別々のオブジェクトとして扱われるため、postをたどってuserのnameを変更できない。

inverse_ofが効いている場合、双方向に関連付けが行われて同じオブジェクトになる。

今まで書いた記憶がない。。。

Rails4.1以降、Railsが自動でつけてくれるようになった。
それ以前は都度SQLを発行してインスタンスを生成していた。
そのため、以下のようなことが起こり得た。

・ 同じレコードを参照しているはずなのに、データを取得するためにSQLが発行される
・ 更新したレコードの内容が、アソシエーション経由だと反映されない

しかし、自動で付けてくれるならなぜ警告されたんだ。

:inverse_ofを明示的に指定すべきパターンとは?

foreign_keyやscopeが指定されている場合など、inverse_ofを明示的に指定しないとこのオプションが効かないパターンがある。

foreign_key

次のように、foreign_keyを指定して関連付けされている場合、inverse_ofが自動で適用されないため明示する必要がある。

model
class User < ApplicationRecord
  has_many :posts, foreign_key: :id
end

class Post < ApplicationRecord
  belongs_to :user
end
user = User.first
post = user.posts.first
# user.name と post.user.name は同じオブジェクトを見ていて欲しい

user.name = "変更" # user.nameを変更
user.name == post.user.name #=> false

scope

次のように、scopeを含む場合は自動でinverse_ofが適用されないため、明示する必要がある。

model
class User < ApplicationRecord
  has_many :posts, -> () { order(:created_at) }
end

class Post < ApplicationRecord
  belongs_to :user
end
user = User.first
post = user.posts.first
# user.name と post.user.name は同じオブジェクトを見ていて欲しい

user.name = "変更" # user.nameを変更
user.name == post.user.name #=> false

どう書くか?

has_many - belongs_toの場合は、
belongs_toで紐付けされているモデルを設定すれば良い。

model
class User < ApplicationRecord
  has_many :posts, -> () { order(:created_at) }, inverse_of: :user
end

class Post < ApplicationRecord
  belongs_to :user
end
7
2
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
2