概要
多対多のアソシエーションを作る際、中間テーブルが必要になります。今回は①モデルの定義 ②中間テーブルの作成 ③データの引き出し方についてまとめます。
なぜ中間テーブルが必要?
例えばCustomer has many teams かつ Team has many customersのような多対多の状況を考えてみます。中間テーブルを使わなければ、下画像のようになります。
1つのカラムに複数のデータを入れることができないため、Team数が増えるためその数だけカラムが増えていきます。これは現実的ではありませんね。
なのでcustomer_idとteam_idのみ保存する中間テーブルを作成すると、無駄なカラムも増えません。
準備編
今回は記載しませんが、ScaffoldのAPIモードでアプリの雛形を作成しています。
1.モデルの作成
Customer / Team / CustomerTeamを作成します。
[Customerモデルの作成]
rails g model Customer name:string age:integer
[Teamモデルの作成]
rails g model Team name:string
[CustomerTeamモデルの作成]
rails g model CustomerTeam customer:references team:references
2.マイグレーションファイルを確認
1の作業で下記のようなマイグレーションが作成されます。
[CustomerTable]
class CreateCustomers < ActiveRecord::Migration[6.0]
def change
create_table :customers do |t|
t.string :name
t.integer :age
t.timestamps
end
end
end
[TeamTable]
class CreateTeams < ActiveRecord::Migration[6.0]
def change
create_table :teams do |t|
t.string :name, null: false
t.timestamps
end
end
end
[CustomerTeamTable]
class CreateCustomerTeams < ActiveRecord::Migration[6.0]
def change
create_table :customer_teams do |t|
t.references :customer, foreign_key: true
t.references :team, foreign_key: true
t.timestamps
end
end
end
migrationを実行してテーブルを作成します。
rails db:migrate
3.モデルの更新
アソシエーションを構築するため、各モデルを更新します。
[customer.rb]
class Customer < ApplicationRecord
accepts_nested_attributes_for :orders, allow_destroy: true
has_many :customer_teams
has_many :teams, through: :customer_teams
end
teamのデータを作成するのと同時に、customer_teamテーブルへデータを作成するように『accepts_nested_attributes_for』を使用しています。使い方は別の記事にまとめているので、参考にくださいね。
[team.rb]
class Team < ApplicationRecord
has_many :customer_teams
has_many :customers, through: :customer_teams
accepts_nested_attributes_for :customer_teams, allow_destroy: true
end
CustomerTeamは1つのcustomerと1つのteamに属しているので、belongs_toを使います。
[customer_team.rb]
class CustomerTeam < ApplicationRecord
belongs_to :customer
belongs_to :team
end
4.seedファイルを作成&実行
seedファイルを作成します。
10.times do |n|
name = (0...8).map{ (65 + rand(26)).chr }.join
age = Random.new().rand(15..65)
id = n + 1
customer = Customer.new
if customer.new_record?
customer.name = name
customer.age = age
customer.save!
end
team_name = (0...5).map{ (65 + rand(26)).chr }.join
team = Team.new
if team.new_record?
team.name = team_name
team.save!
end
end
Customer.all.each do | customer |
id = customer.id
Team.all.each do | team |
team_id = team.id
CustomerTeam.create({customer_id: id, team_id: team_id})
end
end
データを作成します。
rails db:seed
確認編
中間テーブルを作って、多対多の関係性ができているか確認します。先程Seedファイルで作成したデータをコンソールで使ってみましょう。
irb(main):000:0> customer = Customer.first
#実行結果
#<Customer:0x00007f63c7c3a0f0
id: 1,
name: "KPMJKVMV",
age: 61,
created_at: Tue, 28 Dec 2021 04:52:18 UTC +00:00,
updated_at: Tue, 28 Dec 2021 04:52:18 UTC +00:00>
irb(main):001:0> customer.customer_teams
#実行結果
[#<CustomerTeam:0x00007f63c5062798
id: 1,
customer_id: 1,
team_id: 1,
created_at: Tue, 28 Dec 2021 04:52:18 UTC +00:00,
updated_at: Tue, 28 Dec 2021 04:52:18 UTC +00:00>,
#<CustomerTeam:0x00007f63c508b008
id: 2,
customer_id: 1,
team_id: 2,
created_at: Tue, 28 Dec 2021 04:52:18 UTC +00:00,
updated_at: Tue, 28 Dec 2021 04:52:18 UTC +00:00>,
.....(省略)
irb(main):002:0> team = customer.customer_teams.where(team_id: 1)
irb(main):003:0> team.name
#実行結果
=> "CustomerTeam"
中間テーブル経由でteamの情報も取得できることが分かりました。
解説は以上です。