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モデルのインスタンスを、重複なく取得できる。
また補足として、「多対多」の関係でモデルを結ぶ場合は、クロステーブルが必要である。
以上