多対多のアソシエーションについて
進捗報告
多対多の関係について復習しました。
理解した気になっていて理解しきれていないことがあったため、改めて知識の整理を行いました。
■理解したもの
多対多の関係
多対多とは
多対多とは、データベースのテーブル同士の関係の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つのテーブルを経由してインスタンスを取得することができる。