ハッカーを目指す白Tのブログ

夏は白のTシャツを着ています。ラジオが好きです。(ラジオネーム: 隠れキリジダン)

Railsのモデルのリレーションについて

Railsで何か新しいapplicationを作る際やモデルを追加する際に、あらかじめ定義したモデル同士の関係をどのように記述するのだろうか。
今日は、その記述、すなわち、モデル間のリレーションの仕方について解説する。

モデル間のリレーションの3つのタイプ

モデル間のリレーションには以下の3つのタイプがある。


1.1対1

2.1対多

3.多対多



以下、この3つのリレーションのタイプについて順に説明するが、まず説明を簡単にするため、想定するモデルとその関係について述べる。モデルは、Category・Article・Comment・Commenter・Profileで、その関係は、Categoryが複数のArticlesを持ち、ArticleがひとつのCategoryに紐付き、複数のCommentsを持ち、CommentがひとつのArticleとCommenterに紐付き、Commenterが複数のCommentsとひとつのProfileを持ち、ProfileがひとつのCommenterに紐づくものとする。

1.1対1

モデル間を「1対1」の関係で結びたいときに使うメソッドは、has_one/belongs_toである。
今回想定するモデルでは、CommenterとProfileの関係がそれにあたる。
この、モデル間のリレーションは以下のように記述する。

#Commenter.rb
class Commenter < ActiveRecord::Base
  has_one :profile 
end
#一つのprofileを下位に紐付けるということ

#Profile.rb
class Profile < ActiveRecord::Base
  belongs_to :user
end
#一つのcommenterを上位に紐付けるということ

これにより、

Commenter.where(id: 1).first.profile

とすることで、idが1のCommenterモデルのインスタンスに紐づく、Profileモデルのインスタンスを取得できる。

2.1対多

モデル間を「1対多」の関係で結びたいときに使うメソッドは、has_many/belongs_toである。
今回想定するモデルでは、CategoryとArticle、ArticleとComment、CommenterとCommentの関係がそれにあたる。
これらのモデル間のリレーションは、CategoryとArticle間の関係を例にとると、以下のように記述する。

#Category.rb
class Category < ActiveRecord::Base
  has_many :articles 
end

#複数のarticlesを下位に紐付けている

#Article.rb
class Article < ActiveRecord::Base
  belongs_to :Category
end

#ひとつのcategoryを上位に紐づけている

これにより、

Category.where(id: 1).first.articles

とすることで、idが1のCategoryモデルのインスタンスに紐づく、複数のArticleモデルのインスタンスを取得できる。


3.多対多

モデル間を「多対多」の関係で結びたいときに使うメソッドは、has_many/throughである。
今回想定するモデルでは、CategoryとCommenter、ArticleとCommenterの関係がそれにあたる。
これらのモデル間のリレーションは、CategoryとCommenterの関係を例にとると、以下のように記述できる。

#Category.rb
class Category < ActiveRecord::Base
  has_many :articles 
  has_many :comments, through: articles
  has_many :commenter, -> { uniq }, through: comments
end

#Commenter.rb
Class Comment < ActiveRecord::Base
  has_many : comments
end

ここで、注意すべき点がひとつある。それは、-> { uniq } というオプションをどのような時に付けるのかということである。
-> { uniq } オプションを付けるのは、今回の場合、Categoryモデルにおいて、Commentモデルの複数インスタンスを通して、Commenterモデルのインスタンスを取得するときに、同じcommenterが、同じarticleに複数のcommentをした場合、複数回同じCommenterモデルのインスタンスを取得してしまう可能性があるからである。このように、重複するインスタンスを取得してしまう可能性がある場合にのみ、その重複を解除するために、 -> { uniq } オプションを付ける必要がある。
これにより、

Category.where(id: 1).first.commenters

とすることで、idが1のCategoryモデルのインスタンスに紐づく、複数のCommenterモデルのインスタンスを、重複なく取得できる。

また補足として、「多対多」の関係でモデルを結ぶ場合は、クロステーブルが必要である。

以上