11
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

【Rails】 inverse ofオプションについて

Posted at

rubocopを実行していたらRails/InverseOfという形で怒られたので、inverse_ofとはなんぞやというところから分かったことをアウトプットしていきたいと思います。

inverse_ofオプションとは

Railsガイドによると、双方向の関連付けに使われ、rails4.1以降では指定せずともrails側が自動でつけてくれるが、自分で定義した関連には明示的にオプションを付ける必要があるとのこと。つけないと意図しない挙動をする可能性があるようです。

双方向の関連付けについて詳しくはこちら
https://railsguides.jp/association_basics.html#%E5%8F%8C%E6%96%B9%E5%90%91%E9%96%A2%E9%80%A3%E4%BB%98%E3%81%91

今回扱う関連モデル

簡潔にするため、必要ないオプションは省略しています。また、この後出てくるpryでの実行結果においてもSQLの出力は省略してあります。

user.rb
has_many :sold_tickets, class_name: 'Ticket', foreign_key: 'seller_id'
ticket.rb
belongs_to :seller, class_name: 'User'

inverse_ofオプションなしの場合

コンソールを立ち上げて、ニックネームがさかいのuserを取得します。

pry(main)> user = User.find(1)
=> #<User id: 1, email: "aaa@example.com", nickname: "さかい", created_at: "2020-11-19 21:18:56", updated_at: "2020-12-06 04:29:04">

次にさかいの出品したチケットを1枚取得します。

pry(main)> ticket = user.sold_tickets.first
=> #<Ticket:0x00007fd018ff31e8

ニックネームを酒井に変更します。

pry(main)> user.nickname = "酒井"
=> "酒井"

すると、同じオブジェクトを参照しているはずのticket.seller.nicknameでは値が書き換わっていません。

pry(main)> user.nickname == ticket.seller.nickname
=> false
pry(main)> ticket.seller.nickname
=> "さかい"

これは別々のオブジェクトが生成されているためです。

pry(main)> user.equal? ticket.seller
=> false

このままでは、意図しないエラーが起きてしまう可能性がありそうです。
さあここでinverse_ofオプションの出番です。

inverse_ofオプションありの場合

user.rb
has_many :sold_tickets, class_name: 'Ticket', foreign_key: 'seller_id', inverse_of : 'seller'

同じようにuserとticketを定義し、ニックネームを酒井に変更します。

pry(main)> user = User.find(1)
=> #<User id: 1, email: "aaa@example.com", nickname: "さかい", created_at: "2020-11-19 21:18:56", updated_at: "2020-12-06 04:29:04">

pry(main)> ticket = user.sold_tickets.first
=> #<Ticket:0x00007fd018ff31e8

pry(main)> user.nickname = "酒井"
=> "酒井"

すると、先程とは違い同一のオブジェクトを参照し、値が書き換わっていることがわかります。つまり、inverse_ofは同一のオブジェクトを参照させるためのオプションだったわけです。

pry(main)> user.nickname == ticket.seller.nickname
=> true
pry(main)> ticket.seller.nickname
=> "酒井"
pry(main)> user.equal? ticket.seller
=> true

ちなみに、Ticketモデルにinverse_ofオプションをつけても同一のオブジェクトを参照しません。

関連定義

user.rb
has_many :sold_tickets, class_name: 'Ticket', foreign_key: 'seller_id'
ticket.rb
belongs_to :seller, class_name: 'User', inverse_of: 'sold_tickets' # 追記

実行結果

pry(main)> user.nickname == ticket.seller.nickname
=> false
pry(main)> user.equal? ticket.seller
=> false

inverse_of 'seller'の「seller」はUserクラスのインスタンスのオブジェクトはsellerメソッドで取得したオブジェクトと同一のものとしますよという意味だったんですね。

以上になります。何かまちがっている箇所等ありましたらご指摘ください。

11
4
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
11
4

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?