WeBlog

Webに関する情報を中心に発信してるブログ

ActiveRecordのinverse_ofについてメモ

inverse_ofとは

inverse_ofを指定した関連づいている2つのモデルでは、お互いに同じインスタンス(オブジェクト)を参照できるようになる。

両方のモデルがメモリ上で同一のインスタンス(オブジェクト)として扱われる。

逆に、inverse_ofの設定が無いと別物として扱われるので、一方のなんらかの変更がもう一方から参照したときに反映されていない事になる。

実際にやってみた

inverse_ofの記載なし

# Article で inverse_ofの記載なし
class Article < ApplicationRecord
  has_many :article_blocks

# ArticleBlockモデル
class ArticleBlock < ApplicationRecord
  belongs_to :article

# コンソール

> article = Article.find_by!(title: 'タイトル_01')
   
=> #<Article:0x00007fb82d7eba20
 id: 1,
 title: "タイトル_01",
 description: "概要_01",
 created_at: Sun, 06 Jun 2021 16:03:35 JST +09:00,
 updated_at: Sun, 06 Jun 2021 17:50:42 JST +09:00>

> article_block = article.article_blocks.first
=> #<ArticleBlock:0x00007fb82c3af7f0
 id: 1,
 article_id: 1,
 blockable_type: "Medium",
 created_at: Sun, 06 Jun 2021 17:46:12 JST +09:00,
 updated_at: Sun, 06 Jun 2021 17:46:12 JST +09:00>

> article.title == article_block.article.title  # 注目
=> true

> article.title = 'タイトル変更'
=> "タイトル変更"

> article.title == article_block.article.title  # 注目
=> false

上記のようにArticleモデルArticleBlockモデルがあります。

一つのArticleモデルは、複数のArticleBlockモデルと紐づいているので、has_many :article_blocksと記載しています。

一つのArticleBlockモデルは、一つのArticleモデルと紐づいているので、belongs_to :articleと記載しています。

まずは、inverse_of:がない場合で実験してみます。

そうすると、1回目のarticle.title == article_block.article.titleの結果はtrueになっています。

しかし、article.title = 'タイトル変更'でtitleを変更してみました。

そして、2回目のarticle.title == article_block.article.titleを実行するとfalseと判定されています。

Articleのフィールドtitleの変更が、ArticleBlockから関連づけでたどった場合に変更が参照できないです。

メモリ上で別インスタンスとして扱われているということです。

inverse_ofの記載あり

# Article で inverse_ofの記載あり
class Article < ApplicationRecord
  has_many :article_blocks, inverse_of: :article

# ArticleBlockモデル
class ArticleBlock < ApplicationRecord
  belongs_to :article

# コンソール
> article = Article.find_by!(title: 'タイトル_01')
=> #<Article:0x00007ff330976d98
 id: 1,
 title: "タイトル_01",
 description: "概要_01",
 created_at: Sun, 06 Jun 2021 16:03:35 JST +09:00,
 updated_at: Sun, 06 Jun 2021 17:50:42 JST +09:00>

> article_block = article.article_blocks.first
=> #<ArticleBlock:0x00007ff331a6c170
 id: 1,
 article_id: 1,
 blockable_type: "Medium",
 created_at: Sun, 06 Jun 2021 17:46:12 JST +09:00,
 updated_at: Sun, 06 Jun 2021 17:46:12 JST +09:00>

> article.title == article_block.article.title  
=> true

> article.title = 'タイトル変更'
=> "タイトル変更"

> article.title == article_block.article.title  # 注目
=> true

Articleモデルにinverse_of: :articleと記載しました。

inverse_ofはhas_manyの方に記載します。

inverse_ofを記載すると、article.title = 'タイトル変更'で変更を加えた後の

article.title == article_block.article.titleの比較がtrueに変わっています。

これは、メモリ上で同一インスタンスとして扱われるようになり、article.titleもしくはarticle_block.article.titleからtitleを変更しても、article.title == article_block.article.titleがtrueを返すようになります。

https://railsguides.jp/association_basics.html#双方向関連付け

https://shoken.hatenablog.com/entry/2015/07/14/095211