Rails

【Rails】双方向の関連付けで使用されるinverse_ofオプションについてまとめてみた

Rails

 

今回は双方向の関連付けで使用されるオプション、inverse_ofについてまとめました。基本的にはinverse_ofオプションを指定せずとも双方向からデータを参照することができます。一方で少し複雑な条件下では明示的にinverse_ofオプションを記述する必要があるため注意しましょう。

双方向の関連付けとは

双方向の関連付け(アソシエーション)とは、2つのモデル間のリレーションを表す仕組みのことです。代表例としてはhas_manybelongs_toが挙げられます。

例えば、筆者モデル(Auther)と本モデル(Book)が1対多の関係にあるとします。

# app/models/auther.rb
class Author < ApplicationRecord
  has_many :books
end

# app/models/book.rb
class Book < ApplicationRecord
  belongs_to :author
end

 

この関連付けにより、以下のように双方向からデータを参照することができます。

# 筆者に紐付く本のタイトルが取得できる
> a = Author.first
> a.books.first.title
=> 'キングダム'

# 本に紐付く筆者が取得できる
> b = Book.first
> b.auther.name
> '原泰久'

inverse_ofオプションとは

Active Recordが提供しているinverse_ofオプションとは、双方向の関連付け(アソシエーション)を明示的に宣言することができるオプションです。

inverse_ofオプションはhas_manyの後に記述します。

 

先ほどの筆者と本の関係性について再度見ていきます。

# app/models/auther.rb
class Author < ApplicationRecord
  has_many :books
end

# app/models/book.rb
class Book < ApplicationRecord
  belongs_to :author
end

 

上記ではinverse_ofオプションが省略されており、実際は以下のような記述になります。

# app/models/auther.rb
class Author < ApplicationRecord
  has_many :books, inverse_of: 'auther'
end

# app/models/book.rb
class Book < ApplicationRecord
  belongs_to :author
end

 

このようにinverse_ofオプションを省略しても、双方向からデータを参照することができる仕組みはRails4.1で導入されました。

inverse_ofオプションが必要な場面

先程の例では、双方向の関連付け(アソシエーション)にinverse_ofオプションを付与しなくても双方向からデータを参照することができると解説しました。

しかしinverse_ofオプションを記述し、明示的に双方向の関連付けを宣言しなければならない場面があります。

それは:through:foreign_keyオプション、カスタムスコープを使用している場合です。これらの場合はActiveRecordが双方向の関連付けを自動認識しません。

 

例えば、以下のようなモデルがあると仮定します。

# app/models/auther.rb
class Author < ApplicationRecord
  has_many :books
end

# app/models/book.rb
class Book < ApplicationRecord
  belongs_to :writer, class_name: 'Author', foreign_key: 'author_id'
end

 

この場合、ActiveRecordが双方向の関連付けを自動認識できないため、BookからAutherのデータを参照することができません。

# 筆者に紐付く本のタイトルが取得できる
> a = Author.first
> a.books.first.title
=> 'キングダム'

# 本に紐付く筆者が取得できる
> b = Book.first
> b.auther.name
> Traceback (most recent call last):
            1: from (irb):4
    NoMethodError (undefined method `name' for nil:NilClass)

 

BookからAutherのデータを参照したい場合は、以下のようにinverse_ofオプションを追加することで、双方向からのデータ参照を行うことができるようになります。

# app/models/auther.rb
class Author < ApplicationRecord
  has_many :books, inverse_of: 'writer'
end

# app/models/book.rb
class Book < ApplicationRecord
  belongs_to :writer, class_name: 'Author', foreign_key: 'author_id'
end
# 筆者に紐付く本のタイトルが取得できる
> a = Author.first
> a.books.first.title
=> 'キングダム'

# 本に紐付く筆者が取得できる
> b = Book.first
> b.auther.name
> '原泰久'

 

:through:foreign_keyオプション、カスタムスコープを使用している場合は、has_manyの関連付けを宣言する際にinverse_ofオプションを忘れずに付与するようにしましょう。

まとめ

  • inverse_ofとは双方向の関連付けを明示的に宣言することができるオプション
  • inverse_ofオプションはhas_manyの後に宣言する
  • inverse_ofを省略しても、双方向からデータを参照することができるような仕組みはRails4.1で導入された
  • :through:foreign_keyオプション、カスタムスコープを使用している場合は、ActiveRecordが双方向の関連付けを自動認識しないため、明示的にinverse_ofオプションを宣言する必要がある

参考

Railsガイド 関連付けのスコープを制御する

 

 

今回は双方向の関連付けで使用されるオプション、inverse_ofについてまとめました。複雑な関連付けを使用している場合は、inverse_ofオプションを忘れずに指定するようにしましょう。