Posted on: Written by: K-Sato
⚠️ This article was posted over 2 years ago. The information might be outdated. ⚠️

Table of Contents

Associations

本記事で扱う Associations

  • One to One
  • One to Many
  • Many to Many(has_many_through)
  • Many to Many (has_and_belongs_to_many)
  • Plymorphic One to Many

One to One

One to Oneはモデル同士に「1対1」関係がある事を示す。
モデル A の1つのレコードがモデル B の1つのインスタンスを丸ごと所有している。

例えば、1人のユーザーが1つのプロファイルを持つ場合。

各テーブルのカラム

# CreateUsers
class CreateUsers < ActiveRecord::Migration[5.1]
  def change
    create_table :users do |t|
      t.timestamps
    end
  end
end

# CreateProfiles
class CreateProfiles < ActiveRecord::Migration[5.1]
  def change
    create_table :profiles do |t|
      t.string :name
      t.references :user
      t.timestamps
    end
  end
end

各モデル

# app/models/user.rb
class User < ApplicationRecord
  has_one :profile
end

# app/models/profile.rb
class Profile < ApplicationRecord
  belongs_to :user
end

One to One で頻繁に使用されるメソッド

user.profile :該当ユーザーのプロファイル情報を取得
user.build_profile :該当ユーザーのプロファイルを作成(DBに保存しない)
user.create_profile :該当ユーザーのプロファイルを作成(DBに保存する)

One to Many

One-to-Manyは他のモデルとの間に「1対多」の関係がある事を示す。
モデル A の1つのレコードが0個以上のモデル B のインスタンスを所有している。

例えば、1人のユーザー(user)が複数の投稿(post)を持つ場合。

各テーブルのカラム

# CreateUsers
class CreateUsers < ActiveRecord::Migration[5.1]
  def change
    create_table :users do |t|
      t.timestamps
    end
  end
end

# CreatePosts
class CreatePosts < ActiveRecord::Migration[5.1]
  def change
    create_table :posts do |t|
      t.string :title
      t.references :user
      t.timestamps
    end
  end
end

各モデル

# app/models/user.rb
class User < ApplicationRecord
  has_many :posts
end


# app/models/profile.rb
class Post < ApplicationRecord
  belongs_to :user
end

One to Many で頻繁に使用されるメソッド

user.posts :該当ユーザーの全ての投稿を取得。
user.posts << post :新しい関係を作成する。
user.posts.build({}) :該当ユーザーの投稿を作成する。(DBに保存しない)
user.posts.create({}) :該当ユーザーの投稿を作成する。(DBに保存する)
post.build_user :ユーザー作成。(DBに保存しない)
post.create_user :ユーザー作成。(DBに保存する)

Many to Many(has_many_through)

Many-to-Manyは他のモデルとの間に「多対多」の関係がある事を示す。
モデル A とモデル B 共に相互の複数のインスタンスを所有している。
has_many_throughの方式ではモデル C(結合モデル)がモデル A とモデル B を 繋ぐ。

例えば、複数の投稿(post)が複数のタグ(tag)を持つ場合。

各テーブルのカラム

# CreatePosts(モデルA)
class CreatePosts < ActiveRecord::Migration[5.1]
  def change
    create_table :posts do |t|
      t.string :title
      t.timestamps
    end
  end
end

# CreateTags(モデルB)
class CreateTags < ActiveRecord::Migration[5.1]
  def change
    create_table :tags do |t|
      t.string :name
      t.timestamps
    end
  end
end

# CreateTaggings(モデルC)
class CreateTaggings < ActiveRecord::Migration[5.1]
  def change
    create_table :taggings do |t|
      t.references :tag
      t.references :post
      t.timestamps
    end
  end
end

各モデル

# app/models/post.rb
class Post < ApplicationRecord
  has_many :taggins
  has_many :tags, through: :taggings
end

# app/models/tag.rb
class Tag < ApplicationRecord
  has_many :taggins
  has_many :posts, through: :taggings
end

# app/models/tagging.rb
class Tagging < ApplicationRecord
  belongs_to :tag
  belongs_to :post
end

Many to Many(has_many_through)で頻繁に使用されるメソッド

post.tags :該当投稿の全てのタグを取得。
post.tags << tag :新しい関係を作成する。
post.tags << [tag1, tag2] :新しい関係を作成する。
post.tag_ids :該当投稿のタグのIDを全て取得する。
post.tags.destroy(tag) :該当投稿からtagを消去する。
post.taggings :該当投稿の全てのtaggingsを取得。
post.taggings.build(tag_id: tag.id) :投稿とtagの関係を作成する。(DBに保存しない)
post.taggings.create(tag_id: tag.id) :投稿とtagの関係を作成する。(DBに保存する)
post.tags.build({}) :該当投稿と結合モデル(tagging)で結びつくタグを作成する。(DBに保存しない)
post.tags.create({}) :該当投稿と結合モデル(tagging)で結びつくタグを作成する。(DBに保存する)
tag.posts.build({}) :該当タグと結合モデル(tagging)で結びつく投稿を作成する。(DBに保存しない)
tag.posts.create({}) :該当タグと結合モデル(tagging)で結びつく投稿を作成する。(DBに保存する)

Many to Many(has_and_belongs_to_many)

Many-to-Manyは他のモデルとの間に「多対多」の関係がある事を示す。
モデル A とモデル B 共に相互の複数のインスタンスを所有している。
has_and_belongs_to_manyの方式では1つの JOIN テーブルを介してモデル A とモデル B が互いのインスタンスを多数所有し合う。。

例えば、複数の投稿(post)が複数のタグ(tag)を持つ場合。

各テーブルのカラム

# CreateUsers(モデルA)
class CreateUsers < ActiveRecord::Migration[5.1]
  def change
    create_table :users do |t|
      t.timestamps
    end
  end
end

# CreateRoles(モデルB)
class CreateRoles < ActiveRecord::Migration[5.1]
  def change
    create_table :roles do |t|
      t.string :name
      t.timestamps
    end
  end
end

# CreateRolesUsers
class CreateRolesUsers < ActiveRecord::Migration[5.1]
  def change
    create_table :roles_users, id: false do |t|
      t.references :user, foreign_key: true
      t.references :role, foreign_key: true
    end
  end
end

各モデル

# app/models/user.rb
class User < ApplicationRecord
  has_and_belongs_to_many :roles
end


# app/models/tag.rb
class Role < ApplicationRecord
  has_and_belongs_to_many :users
end

Many-to-Many(has_and_belongs_to_many)で頻繁に使用されるメソッド

Many-to-Many(has_many_through)で頻繁に使用されるメソッドを参照。

Polymorphic

Polymorphicであるモデル A が他の複数のモデル(B,C,D)に属していることを、1 つの関連付けだけで表現することが可能。

例えば、コメント(picture)が投稿(post)がとユーザー(user)に所属している場合。

各テーブルのカラム

# CreateUsers(モデルA)
class CreateUsers < ActiveRecord::Migration[5.1]
  def change
    create_table :users do |t|
      t.timestamps
    end
  end
end

# CreatePosts(モデルB)
class CreatePosts < ActiveRecord::Migration[5.1]
  def change
    create_table :posts do |t|
      t.string :title
      t.references :user
      t.timestamps
    end
  end
end

# Createpictures(モデルC)
class Createpictures < ActiveRecord::Migration[5.1]
  def change
    create_table :pictures do |t|
      t.string :body
      t.integer :imageable_id
      t.string :imageable_type
      t.timestamps
    end
  add_index :pictures, [:imageable_type, :imageable_id]
  end
end

以下のようにreferencesを使用してより簡潔に書くことも可能。
# Createpictures(モデルC)
class Createpictures < ActiveRecord::Migration
  def change
    create_table :pictures do |t|
      t.text :body
      t.references :imageable, polymorphic: true, index: true
      t.timestamps
    end
  end
end

各モデル

# app/models/user.rb
class User < ApplicationRecord
  has_many :posts
   has_many :pictures, as: :imageable
end

# app/models/post.rb
class Post < ApplicationRecord
  belongs_to :user
  has_many :pictures, as: :imageable
end

# app/models/picture.rb
class picture < ApplicationRecord
  belongs_to :imageable, polymorphic: true
end

Polymorphic で頻繁に使用されるメソッド

post.pictures :該当投稿の全てコメントを取得。
user.pictures :該当ユーザーの全てコメントを取得。
post.pictures.build :該当投稿に結びつくコメントを作成。(DBに保存しない)
post.pictures.create :該当投稿に結びつくコメントを作成。(DBに保存する)
user.pictures.build :該当ユーザーに結びつくコメントを作成。(DBに保存しない)
user.pictures.create :該当ユーザーに結びつくコメントを作成。(DBに保存する)
post.pictures << picture.new({}) :該当ポストにコメントを関連づけ。
user.pictures << picture.new({}) :該当ユーザーにコメントを関連づけ。

以下のような Image ができるイメージ。

[
  {id: 1, name: "ss", imageable_id: 1, imageable_type: "Post"},
  {id: 2, name: "ss", imageable_id: 1, imageable_type: "User"}
]

References

About the author

I am a web-developer based somewhere on earth. I primarily code in TypeScript, Go and Ruby at work. React, RoR and Gin are my go-to Frameworks.