多対多のアソシエーションについて

進捗報告

多対多の関係について復習しました。 理解した気になっていて理解しきれていないことがあったため、改めて知識の整理を行いました。

■理解したもの

多対多の関係

多対多とは

多対多とは、データベースのテーブル同士の関係の1つ。 (他に1対1、1対多などがある。) あるテーブルの複数のレコードが別のテーブルの複数のレコードと関連づけられている場合は、多対多の関係と言える。

具体的な例でいうと、questionsテーブルとusersテーブル一があるとする。それぞれ1つのquestionには回答済みuserがたくさん、1つのuserには回答済みquestuionがたくさんあるとき、questionsとusersの関係は多対多となる。

多対多の関係を表現するには、questionとuserの情報両方を持っているような中間テーブルが必要。

中間テーブルには「どのquestionがどのuserと関連づいているか」という情報が全て記載されていることになる。 一つのレコードには「question_id x user_id」の組み合わせが一つ記録され、全てのquestionとuserの組み合わせの数分、レコードが蓄積されていく。10個の質問にそれぞれ3人ずつ回答している場合、すべての関係性を表すのに中間テーブルのレコードは30個生成されることとなる。

中間テーブルにquestion_id、user_id以外のカラムを持たせることも可能。(中間テーブルにtextカラムを作り、questionに対するuserの回答を保存したり。)


has_many throughオプション

has_manyのthroughオプションはモデルに多対多の関連を定義するときに利用する。throughの名前通り「〜を経由する」という意味。

例)

questionsとusersが多対多の関係、answersが中間テーブルの時。

app/models/question.rb

class Question < ActiveRecord::Base

has_mahy :answers

has_many :users, through: :answers

end

app/models/user.rb

class User < ActiveRecord::Base

has_many :answers

has_many :questions, through: :answers

end

app/models/answer.rb

class Answer < ActiveRecord::Base

belongs_to :question

belongs_to :user

end


この状態ではquestionに回答済みのユーザー情報を

userのインスタンス.questions

で取得できる。



でもこれでは、userの投稿したquestionsとの関係を、userモデルファイルにhas_many :questionsと定義し、userの投稿したquestionを取り出す際、

userのインスタンス.questions

とすることとなり、ごっちゃになる。

そのため、

class User < ActiveRecord::Base

has_many :answerd_questions, through: :answers, source: :question

とすると、

userのインスタンス.answerd_questions

として回答済みquestionsを取得できる。


has_many/belongs_toのみで多対多の情報取得するとき

ユーザーが答えた回答を取得

answers = Answer.where(user_id: 1)

answersのquestion_idカラムの値で配列にする pluckメソッドで任意のカラムを配列として取得できる。

question_ids = answers.pluck(:question_id)

ユーザーが答えた質問を重複なく取得する。

Question.find(question_ids)

上記のようにすれば取得することが可能だがステップが多く面倒。

なので多対多の関係をモデルに定義してあげる方がよい。

has_manyにthroughオプションを指定することで1つのテーブルを経由してインスタンスを取得することができる。