kasei_sanのブログ

かせいさんのIT系のおぼえがきです。胡乱の方はnoteとtwitterへ

Railsガイド Active Record の関連付け(1.関連付けを使用する理由〜2.関連付けの種類)

Active Record の関連付け (アソシエーション) | Rails ガイド

関連付けのメリット

  • Railsがテーブル間の関連を知ることで、必要な作業を勝手にやってくれる
  • 例えば、顧客と注文が関連していたら、顧客の情報を消した時に、顧客に紐付いた注文を全部消してくれる(dependent: :destroy を定義した場合)

関連付けの種類

  • belongs_to
  • has_one
  • has_many
  • has_many :through
  • has_one :through
  • has_and_belongs_to_many

1対1

  • belongs_to, has_one
  • belongs_to : 従属
  • 1人のUserに対して、Profileは1つ
  • belongs_to 側が相手のidを持つ(マイグレーション時に勝手に作ってくれる)
  • 今回ならば、user_id

model

class User < ActiveRecord::Base
  has_one :profile # 主
end
class Profile < ActiveRecord::Base
  belongs_to :user # 従
end

作ってみる

rails g model user name:string
rails g model profile address:string user:belongs_to
class CreateUsers < ActiveRecord::Migration
  def change
    create_table :users do |t|
      t.string :name

      t.timestamps null: false
    end
  end
end

class CreateProfiles < ActiveRecord::Migration
  def change
    create_table :profiles do |t|
      t.string :address
      t.belongs_to :user, index: true, foreign_key: true

      t.timestamps null: false
    end
  end
end
sqlite> .schema users
CREATE TABLE "users" (
  "id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
  "name" varchar,
  "created_at" datetime NOT NULL,
  "updated_at" datetime NOT NULL);
sqlite> .schema profiles
CREATE TABLE "profiles" (
  "id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL,
  "address" varchar,
  "user_id" integer,
  "created_at" datetime NOT NULL,
  "updated_at" datetime NOT NULL);
CREATE INDEX "index_profiles_on_user_id" ON "profiles" ("user_id");

1対多

  • belongs_to, has_many
  • 1人のUserに対して、Orderは複数
class User < ActiveRecord::Base
  has_many :order # 主
end
class Order < ActiveRecord::Base
  belongs_to :user # 従
end

has_mamy と has_one は、Rails上の制約でしか無い?

多対多

  • through オプション を使う
  • Appointment(予約)、Physician(医者)、Patient(患者)
  • Physicianは、Appointmentsを経由して、Patientsに紐付いている has_many :patients, through: :appointments
class Physician < ActiveRecord::Base
  has_many :appointments
  has_many :patients, through: :appointments
end
 
class Appointment < ActiveRecord::Base
  belongs_to :physician
  belongs_to :patient
end
 
class Patient < ActiveRecord::Base 
  has_many :appointments
  has_many :physicians, through: :appointments
end

through オプション を使った、ネストした関連のショートカット

class Document < ActiveRecord::Base
  has_many :sections
  has_many :paragraphs, through: :sections
end
 
class Section < ActiveRecord::Base
  belongs_to :document
  has_many :paragraphs
end
 
class Paragraph < ActiveRecord::Base
  belongs_to :section
end
  • Document 1 -> * Section 1 -> * Paragraph
  • has_many :paragraphs, through: :sectionsを書くことで、Documentは、Sectionを通して、Paragraphと、関連していることを定義
  • すると、@document.paragraphs が呼べるようになる

has_and_belongs_to_many

  • has_mamy: through と同じ多対多だけど、modelを介在しない
  • 〜_id だけを持つテーブルは使う
  • そのテーブルはRails上は、modelを持たない

ポリモーフィック関連付け

  • belongs_to の参照先を抽象化する
class Picture < ActiveRecord::Base
  belongs_to :imageable, polymorphic: true
end

class Employee < ActiveRecord::Base
  has_many :pictures, as: :imageable
end

class Product < ActiveRecord::Base
  has_many :pictures, as: :imageable
end

この場合、Picture は、belongs_to を2個書く必要は無い

ポリモーフィック関連付け用のマイグレーション

rails g model picture name:string imageable:references

自己結合

  • 自分で自分を関連付け
  • 従業員(Employee)モデルで、マネージャーと部下を関連付けたい時など
class Employee < ActiveRecord::Base
  has_many :subordinates, class_name: "Employee", foreign_key: "manager_id"
  belongs_to :manager, class_name: "Employee"
end

マイグレーション

rails g model employee name:string manager:references