ここではRailsの「アソシエーション」についてまとめたいと思います。これまでn回書かれてきた話題ですが、ここでは私なりに勉強して学んだこととしてアウトプットしたいと思います。
Contents
アソシエーションとは
Railsの「関連付け(アソシエーション: association)」は、2つのActive Recordモデル同士のつながりを指します。 (Active Record の関連付け – Railsガイド)
言い換えるなら、データベースのテーブル同士の関係(1対1/1対多/多対多など)のRailsでの表現ということですね。
アソシエーションの種類
belongs_to
belongs_toは、そのテーブルが引数に指定した対象テーブルに所属するときに使います。
belongs_to({対象モデル名} [, scope, options])
このbelongs_toを指定すると、指定した側のクラスに対象テーブルに紐つく外部キーが作成され、自テーブル→対象テーブルという1方向の「1対1関係」がつくられます。つまり「自テーブルは対象テーブルを知っている」が、「対象テーブルは自テーブルについて知らない」状態になります。
双方向のアソシエーションは、後述のhas_oneやhas_manyを併用して実現します。
指定できるオプション
:class_name
関連するモデルクラス名を指定。関連名と参照先のクラス名を分けたいとき(後述)に利用します:dependent
:destroyで参照先レコードが削除される場合に、参照元レコードもDBから削除:foreign_key
参照先のテーブルの外部キーのカラム名:primary_key
参照元(自テーブル)の外部キーのカラム名:optional
trueを指定すると参照先テーブルに該当データがない場合もエラーにならない:required
trueを指定すると関連づけの確認がなされる。:required false
はあまり使わず同等の:optional true
を使う方が良さそう (→Rails5からbelongs_toアソシエーションの挙動が変わった – SHOYAN BLOG)
オプションについてより詳しくは、こちらに記載があります。
「関連名と参照先のクラス名を分ける」?
以下のように書いたときの:admin_user
が「関連名」、User
が「参照先のクラス名」です。
belongs_to :admin_user, class_name: 'User'
has_one
has_one
は自テーブルが対象テーブルを1つ持っている場合に使います。どういうことでしょう。belongs_toとの比較でいうと、「has_oneは対象テーブルが自テーブルに対する外部キーを持つ」という場合に利用します。一方で先述の通り、belongs_toは「自テーブルが対象テーブルに対する外部キー」を持ちます。つまり先ほども書いたように、belongs_toとhas_oneとを併用することで双方向の関連づけを定義でき、この場合は双方向「1対1」関係となります。
has_one(関連モデル名 [, scope ,オプション])
has_many
has_manyはその名の通り、対象モデルとの「1対多」関係をなします。例えば以下のように実装した時、:authorが「1」、:booksが「多」です。
class Author < ApplicationRecord has_many :booksend---class Book < ApplicationRecordbelongs_to :authorend
has_many :through
has_many :thoughは、他方のモデルと「多対多」関係を設定する際に利用されます。RDBでは自テーブルと対象テーブルとを「多対多」関係で結ぶ時、それぞれが中間テーブルと「1対多」関係をなすことで実現しますが、railsではその中間テーブルを:throughで指定することで実装します。
例えば以下は、userとsessionとをsession_participantを中間テーブルにして「多対多」にしています。
class User < ApplicationRecordhas_many :session_participantshas_many :sessions, through: :session_participantsend---class Session < ApplicationRecordhas_many :session_participantshas_many :users, though: :session_participantsend---class SessionParticipant < ApplicationRecordhas_many :usershas_many :sessionsend
ちなみに「多対多」はhas_and_belongs_to_many
を用いても以下のように実現できます。
class User < ApplicationRecordhas_and_belongs_to_many :sessionsend---class Session < ApplicationRecordhas_and_belongs_to_many :usersend
参考
belongs_to/has_one/has_manyの簡単まとめ
テーブルの関連付け(一対一、一対多、多対多) – subaru-hello’s blog
Active Record の関連付け – Railsガイド
Rails5からbelongs_toアソシエーションの挙動が変わった – SHOYAN BLOG
ActiveRecord::Associations::ClassMethods