💭

RailsでのRDB設計の書き方と考え方(1対多など)

2023/11/29に公開

RDB設計とは

リレーショナルデータベースの構造を定義するものです。
この記事では「一対一」「一対多」「多対多」のリレーションの具体例、書き方を解説します。

モデルの書き方

具体的にどう書いていくのか説明していきます。

Userと一対多の関係

1人のユーザーと一対多の関係を書いていきます。
例えば日報なら1人のユーザーは日報を複数投稿します。
このように1人に対して複数のものが存在するのが一対多の関係です。

1.ユーザーと日報

モデル
日報(Daily Report)の一対多の関係を考える場合、例えば、1つのユーザーが複数の日報を持つといったケースがあります。以下は、これを実現するための例です。

class DailyReport < ActiveRecord::Base
  belongs_to :user
end
class User < ActiveRecord::Base
  has_many :daily_reports
end

上記のコードでは、Userモデルは複数のDailyReportを持つ関係を示すhas_manyを使用し、逆にDailyReportモデルは1つのUserに属することを示すbelongs_toを使用しています。

  • マイグレーション:
    DailyReportモデルのマイグレーションファイルで、user_idを格納するための外部キーを追加します。Userモデルの中身も見ていきます。
class CreateUsers < ActiveRecord::Migration[7.0]
  def change
    create_table :users do |t|
      t.string :slack_id
      t.string :slack_name
     
      t.datetime :deleted_at, default: nil

      t.timestamps

    end
  end
end
class CreateDailyReports < ActiveRecord::Migration[7.0]
  def change
    create_table :daily_reports do |t|
      t.references :user, null: false, foreign_key: true
    #他のカラムも追加できる

      t.timestamps
    end
  end
end

これを他の具体例も見ながら書いていきます。

2.ブログ記事とコメント:
一つのブログ記事(BlogPost)に対して、複数のコメント(Comment)が存在します。これは、一つのブログ記事が複数の読者からコメントを受けるよくある例です。

class BlogPost < ActiveRecord::Base
  has_many :comments
end
class Comment < ActiveRecord::Base
  belongs_to :blog_post
end
class CreateUsers < ActiveRecord::Migration[6.0]
  def change
    create_table :users do |t|
      t.string :name
      t.timestamps
    end
  end
end
class CreateBlogPosts < ActiveRecord::Migration[6.0]
  def change
    create_table :blog_posts do |t|
      t.string :title
      t.text :content

      t.timestamps
    end
  end
end

3.ユーザーと投稿:
一人のユーザー(User)が複数の投稿(Post)を持つ場合。これは、一つのユーザーが複数のコンテンツを作成できるよくある例です。

class User < ActiveRecord::Base
  has_many :posts
end
class Post < ActiveRecord::Base
  belongs_to :user
end
class CreateUsers < ActiveRecord::Migration[6.0]
  def change
    create_table :users do |t|
      t.string :name
      t.timestamps
    end
  end
end
class CreatePosts < ActiveRecord::Migration[6.0]
  def change
    create_table :posts do |t|
      t.string :title
      t.text :content
      t.references :user, foreign_key: true
      t.timestamps
    end
  end
end

1対1

1対1の関係も具体的に見ていきます。

1.部署と責任者
一つの部署を一人の責任者が管理している場合です。

class Department < ActiveRecord::Base
  has_one :head
end
class Head < ActiveRecord::Base
  belongs_to :department
end
class CreateDepartments < ActiveRecord::Migration[6.0]
  def change
    create_table :departments do |t|
      t.string :name
      t.timestamps
    end
  end
end
class CreateHeads < ActiveRecord::Migration[6.0]
  def change
    create_table :heads do |t|
      t.string :name
      t.references :department, foreign_key: true, unique: true
      t.timestamps
    end
  end
end

2.プロジェクトとリーダー
各プロジェクトの中にリーダーが1人いる場合です。

class Project < ActiveRecord::Base
  has_one :leader
end
class Leader < ActiveRecord::Base
  belongs_to :project
end
class CreateProjects < ActiveRecord::Migration[6.0]
  def change
    create_table :projects do |t|
      t.string :name
      t.timestamps
    end
  end
end
class CreateLeaders < ActiveRecord::Migration[6.0]
  def change
    create_table :leaders do |t|
      t.string :name
      t.references :project, foreign_key: true, unique: true
      t.timestamps
    end
  end
end

多対多

1人のユーザーは複数のプロジェクトに参加でき、同時に1つのプロジェクトには複数のユーザーが関わっていることを想定します。下のコードはUserモデルとProjectモデルが多対多の関係です。

class User < ActiveRecord::Base
  has_many :project_memberships
  has_many :projects, through: :project_memberships
end
class Project < ActiveRecord::Base
  has_many :project_memberships
  has_many :users, through: :project_memberships
end
class ProjectMembership < ActiveRecord::Base
  belongs_to :user
  belongs_to :project
end

中間のProjectMembershipモデルを関連付けして、
has_many :throughを使用して、ユーザーとプロジェクトの多対多の関係ができています。

以下がマイグレーションファイルの例です。

class CreateUsers < ActiveRecord::Migration[6.0]
  def change
    create_table :users do |t|
      t.string :name
      t.timestamps
    end
  end
end
class CreateProjects < ActiveRecord::Migration[6.0]
  def change
    create_table :projects do |t|
      t.string :name
      t.timestamps
    end
  end
end
class CreateProjectMemberships < ActiveRecord::Migration[6.0]
  def change
    create_table :project_memberships do |t|
      t.references :user, foreign_key: true
      t.references :project, foreign_key: true
      t.timestamps
    end
  end
end

それ以外の関係

  • 多対1(Many-to-One)
    例えば複数の商品が
    例えば、複数の商品が1つのカテゴリに属している場合などです。

  • 1対0または多対0(One-to-Zero or Many-to-Zero)
    顧客が注文を持っているが、すべての顧客が注文を持っているわけではない時です。

  • 多対1(Many-to-One)
    学生とクラスの関係で1人の学生が複数のクラスに在籍する場合です。

DB設計の有益記事

https://qiita.com/kiyodori/items/5083ad8bbfc232d01827

https://qiita.com/KNR109/items/5d4a1954f3e8fd8eaae7

Discussion