Railsのモデル間の関連を具体的に作成します。関連には、2つのモデル間の関連以外に一つのモデルのインスタンス間(テーブルのデータ間)の関連、いわゆる自己関連というものもあります。
ここでは、2つのモデル間に多対多の関連を作りRails consoleで動作を確認します。
1対1関連については2モデル間の1:1関連を参照してください。
1対多関連については2モデル間の1:多関連を参照してください。
自己関連については自己関連(自己結合)を参照してください。
Model
今回、登場するモデルは、 Userモデル、MeetingモデルとUserMeetingモデル の3つです。 テーブルでいえば、usersテーブル、meetingsテーブルとuser_meetingsテーブル です。このuser_meetingsテーブルを仲立ち(「中間テーブル」といいます)としてUserモデル と Meetingモデル の間に多対多関連を作ります。
2モデル間の多対多関連
会議(会合あるいはパーティのようなもの)に参加する参加者を考えます。 一つの会議(会合あるいはパーティ)には複数の人(users)が参加できます。また逆に、一人の人は複数の会議(meetings)に参加することができます。このような関係を多対多の関連というのでした。
したがって、
- users : meetings は、多対多の関連
となります。
UserモデルとMeetingモデル
Userモデル と Meetingモデル の関連を以下のように作成します。
これら2つのテーブルを結びつけるための中間テーブル(user_meetingsテーブル)を作成します。
もし、usersテーブルとmeetingsテーブルの作成がまだでしたら次のように作ります。
次いで、各モデルに以下を追加記述します。
user_meetingsテーブルに参照先のidが入る「user_id」フィールド( user:references で作られました)と「meeting_id」フィールド( meeting:references で作られました)があるので、 belongs_to を記述します。
has_many :user_meetings によって「Userモデル」と「UserMeetingモデル」が1対多の関連である(user_meetingsテーブルに user_id がある)ことを指定します。 has_many :meetings によって「Meetingモデル」への関連を作りますが「meetingsテーブル」に user_id はありません。そこで :through => :user_meetings によって「UserMeetingモデル」を経由して「Meetingモデル」への関連を作ります。
- meeting.rb Meetingモデルにも同じように記述します。
クラス図ではこのようになります。
:class_name :through :source
先の例ではRailsのお約束に従っているのでいろいろ指定を省略してます。上記と同じ内容を今度は省略しないで記述してみましょう。シンボル名がクラス名やテーブル名と区別できるようにわざと名前を変えて付けます。
ここではRailsのお約束に従わずに勝手に名づけた部分を太字で記述します。
- UserMeetingモデルには、2つの関連があり:user_ref、:meeting_refとしました。 それぞれに結び付けるクラス名(class_name)と外部キー(foreign_key)を指定します。
- Userモデルにも関連が2つあり、UserMeetingモデルとの関連を:umsとして作りました。 :attended_meetingsというMeetingクラスへの関連を、:umsを経由し、その経由先の:meeting_refによって関連付けるよう指定します。
- Userモデルと同じですが、Meetingモデルにも関連が2つあり、UserMeetingモデルとの関連を:u_mとして作りました。 :attended_usersというUserクラスへの関連を、:u_mを経由し、その経由先の:user_refによって関連付けるよう指定します。
結果、 ユーザーインスタンス.attended_meetings でそのユーザーが参加した会議の一覧を取得でき、 会議インスタンス.attended_users でその会議に参加したユーザーの一覧が取得できます。
まとめ – :class_name :through :source –
の意味は、『Rが参照されたとき、その参照先のクラスはAで、そこにいたる経路は参照名Bに記述されたクラスの持つ(複数の)参照のうちCという参照名のものである』となります。
各オプションが省略できるときの条件と省略せずに記述したオプションを表の形のまとめておきます。
オプション名 | 省略できる条件 | 省略せずに記述した時の使用例 | 省略した記述 |
---|
:class_name | 関連名がモデル名の単数形・複数形である | has_one :user, class_name: 'User' has_many :users, class_name: 'User' | has_one :user has_many :users |
---|
:foreign_key | 外部キー名が「'モデル名'_id」である | has_many :users, foreign_key: 'user_id' | has_many :users |
---|
:through | モデルをJOINする必要がない | (UserMeetingモデルをJOINする場合の書き方) has_many :user_meetings has_many :users, through: :user_meetings |
---|
:source | (:through関連を使って)JOINしたモデルの中で 定義されている関連名が約束どおりである | has_many :users, through: :user_meetings, source: :user
class UserMeeting < ActiveRecord::Base belongs_to :user | has_many :users, through: :user_meetings
class UserMeeting < ActiveRecord::Base belongs_to :user |
---|
Rails console で確認
Rails console で確認してみます。
手順
- meeting1(サンプル会議1)にはuser1とuser2が参加します。
- meeting2(サンプル会議2)にはuser1とuser3が参加します。
- uaer4がmeeting1とmeeting2に参加します。
- 会議に参加したユーザーを確認します。
- ユーザーが参加した会議を確認します。
Rails console に打ち込むコマンドは次のものです。 まずは準備
- user1 = User.create(name: ‘ユーザー1’)
- user2 = User.create(name: ‘ユーザー2’)
- user3 = User.create(name: ‘ユーザー3’)
- user4 = User.create(name: ‘ユーザー4’)
- meeting1 = Meeting.create(name: ‘サンプル会議1’)
- meeting2 = Meeting.create(name: ‘サンプル会議2’)
次に、多対多を作ります。
- meeting1.attended_user « user1
- meeting1.attended_user « user2
- meeting2.attended_user « user1
- meeting2.attended_user « user3
- user4.attended_meetings « meeting1
- user4.attended_meetings « meeting2
そして、確認します。
- m1 = Meeting.where(name: ‘サンプル会議1’).first
- m2 = Meeting.where(name: ‘サンプル会議2’).first
- u1 = User.where(name: ‘ユーザー1’).first
- u2 = User.where(name: ‘ユーザー2’).first
- u3 = User.where(name: ‘ユーザー3’).first
- u4 = User.where(name: ‘ユーザー4’).first
- m1.attended_users
- m2.attended_users
- u1.attended_meetings
- u2.attended_meetings
- u3.attended_meetings
- u4.attended_meetings
やってみましょう。準備段階は省きました。多対多を作るところからです。
もう確認しなくてもわかりますが、確認です。
期待どおりの結果でした。
なんて簡単!!やっぱりRailsはすばらしい。