【Rails】Associationsまとめ


概要

Ruby on Railsでよく使用される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