はじめに
Railsなどを中心に勉強中のエンジニア初心者が他の記事を参考にしたり、実際に実装してみたりして、アウトプットの一環としてまとめたものです。
本投稿は、文献を参考にしてまとめてみたものですが、テーブル設計(ER図)については筆者が独自に行なったため、間違っていることもあると思われるので、その際は指摘いただけると幸いです。
アソシエーション(関連付け)とは
アソシエーション(関連付け)とは、テーブル間のリレーションシップをモデル上の関係として操作できるようにする仕組みのこと。アソシエーションを利用することで、複数のテーブルにまたがるデータ操作が、より直感的に利用できるようになる。
ER図
下記のER図を前提として、各アソシエーションについてまとめる。
belongs_to(参照元テーブルから被参照テーブルにアクセスする)
belongs_to
メソッドは、現在のモデルから相手先のモデルを参照する
というアソシエーションを宣言する。
例えば、reservations
→ rooms
テーブルのような関係性。
reservations
テーブルがroom_id
を外部キーにして、rooms
テーブルを参照している。
基本形
参照先のモデル名を単数形で指定する。
複数形で指定すると、エラーになるため注意。
Railsはアソシエーション宣言時の名前から自動的にモデルのクラス名を推測すため、宣言名が複数形になってしまっていると、そこから推測されるクラス名も複数形になってしまう。
# モデルの定義
belongs_to :単数形のモデル名
# 参照先テーブルへのアクセス
モデルオブジェクト.関連名
実装例
# モデルの定義
class Reservation < ApplicationRecord
belongs_to :room
end
# 参照先テーブルへのアクセス
@reservation = Reservation.find(1)
@reservation.room
備考
belongs_to
のみでは、一方向のアソシエーションのみである。
そのためアソシエーションを完成させるためには、双方向にアソシエーションを行う必要がある。
相手側のモデルに対して、has_one
またはhas_many
を指定する。
has_one(1 : 1の関連付けを宣言する)
has_one
メソッドは、1:1(被参照テーブル → 参照先テーブル)
のアソシエーションを宣言する。
相手のモデルがこのモデルへの参照を持っていることを表す。
例えば、1件のユーザ情報(users
)が0、または1件のプロフィール(profile
)をもつような関係性。
基本形
# モデルの定義
has_one :単数形のモデル名
# 参照元テーブルへのアクセス
モデルオブジェクト.関連名
実装例
# Userモデルの定義
class User < ApplicationRecord
has_one :profile
end
# 参照元テーブルへのアクセス
@user = User.find(1)
@user.profile
注意
belongs_to
メソッドとセットで利用することで、参照元/参照先テーブル双方向の関連を表現する。
1:1
のアソシエーションにおいて、主となるモデルにhas_one
を宣言し、従となるモデルにbelongs_to
を宣言するのが基本。
has_many(1 : nの関連を宣言する)
has_many
メソッドは、1:n(被参照テーブル → 参照元テーブル)
のアソシエーションを宣言する。
相手のモデルとの1:多のつながりを表している
例えば、1件のroom
が複数のreservations
を持つような関係を表している。
基本形
# モデルの定義
has_many :複数形のモデル名
# 参照元テーブルへのアクセス
モデルオブジェクト.関連名
実装例
# Postモデルの定義
class Room < ApplicationRecord
has_many :reservations
end
# 参照元テーブルへのアクセス
@room = Room.find(1)
@room.reservations
has_and_belongs_to_many(m : nの関連を宣言する、中間テーブル)
has_and_belongs_to_many
メソッドは、m:n
のアソシエーションを宣言する。
相手モデルのと多対多のつながりを表している
リレーショナルデータベースでは、このような関係は直接表現できないため、中間テーブルを使って表すのが一般的。
例えば、rooms
テーブルとcategories
テーブルとを中間テーブルrooms_categories
が仲介するような関係性。
この時、中間テーブルは主キーもタイムスタンプ(
created_at
やupdated_at
)などは持ってはいけない。
また、アプリケーション側で意識する必要はなく、モデルとして作成する必要もない。
基本形
# モデルの定義
has_and_belongs_to_many :複数形のモデル名
# 参照先テーブルへのアクセス
モデルオブジェクト.関連名
実装例
# Room、Categoryモデルの定義
class Room < ApplicationRecord
has_and_belongs_to_many :catogires
end
class Category < ApplicationRecord
has_and_belongs_to_many :rooms
end
# 参照先テーブルへのアクセス
@room = Room.find(1)
@room.categories
備考
hashas_and_belongs_to_many
メソッドは利用にあたって制限が多いため、できるだけhas_many :through
を優先して利用するのが望ましいらしい。
has_many :through(より複雑なm : nの関連を宣言する)
has_many
メソッドのthrough
オプションを使用することで、より複雑なm:n
のアソシエーションが宣言できる。
このアソシエーションでは、2つのモデルの間に「第3のモデル」が介在し、それを経由(through
)して相手のモデルの「0個以上」のインスタンスとマッチする。
中間テーブルが外部キー以外の情報を持たない場合は
has_and_belongs_to_many
メソッドが利用できる。
例えば、users
テーブルとrooms
テーブルを、中間テーブルreservations
が仲介するような関係性。
基本形
# 経由元のモデルの定義
has_many :複数形のモデル名
has_many :経由先のモデル名(複数形), throguh: :経由するモデル名(複数形)
# 経由するモデルの定義
belongs_to :経由元のモデル名(単数形)
belongs_to :経由先のモデル名(単数形)
# 経由先テーブルへのアクセス
モデルオブジェクト.関連名
実装例
# User、Reservation、Roomモデルの定義
class User < ApplicationRecord
has_many :reservations
has_many :rooms, through: :reservations
end
class Reservation < ApplicationRecord
belongs_to :user
belongs_to :room
end
class Room < ApplicationRecord
has_many :reservations
has_many :users, through: :reservations
end
# 経由先テーブルへのアクセス
@user = User.find(1)
@user.rooms # Reservationモデルを介さずにRoomの情報を取得可能
備考
has_many :through
によるアソシエーションは、ネストしたhas_many
のアソシエーションを介して「ショートカット」を設定することもできる。
例えば、1つの組織(organizaitons
)に複数の提供者(suppliers
)が所属しており、複数の提供者が複数の部屋(rooms
)を提供している状況において、組織に紐づく部屋の情報を提供者をスキップして取得したい場合など。
その場合は、以下のように宣言することができる。
# Organization、Supplier、Roomモデルの定義
class Organization < ApplicationRecord
has_many :suppliers
has_many :rooms, through: :suppliers
end
class Supplier < ApplicationRecord
belongs_to :organization
has_many :rooms
end
class Room < ApplicationRecord
belongs_to :supplier
end
# 経由先テーブルへのアクセス
@organization = Organization.find(1)
@organization.rooms # Supplierモデルを介さずにRoomの情報を取得可能
has_one :through
省略。以下参照。
参考
最後に
いかがでしたでしょうか。
今回はRailsのモデル同士のアソシエーションについてまとめてみました。
ここ違うよ!でしたり、こうした方がいいよ!などがあればコメントいただけると幸いです。
他にも下記のような記事を投稿しております。
興味がありましたら、ぜひご覧ください。