モデル間の関連付け - 1対多の関係を理解しよう

学習の目標

本章では、以下の内容を学習します。

  • 1対多の関連付けの概念を理解する
  • belongs_tohas_manyの役割と使い方を習得する
  • 関連付けのためのマイグレーションファイルの書き方を学ぶ
  • Railsコンソールで関連付けを確認する方法を身につける
  • よくあるミスとその対処法を知る

はじめに

Webアプリケーションを開発していると、データ同士が関連している場合がよくあります。例えば、ブログサイトではユーザーが複数の記事を投稿したり、ECサイトでは顧客が複数の注文を持ったりします。

Railsではこのような関連性を簡単に表現できる機能が用意されています。今回はその中でも最も基本的な1対多の関連付けについて学んでいきましょう。

1対多の関連付けとは

1対多の関連付けとは、あるモデル(親)が複数の別のモデル(子)と関連付けられる関係のことです。

わかりやすい例を挙げてみましょう。ユーザーと投稿(ブログ)の関係を考えてみてください。

  • 1人のユーザーは複数の投稿を持つことができます
  • 1つの投稿は1人のユーザーにのみ属します

このような関係を「1対多の関係」と呼びます。図で表すと以下のようになります。

ユーザー(1) -----< 投稿(多)

ユーザーが一人に対して複数の投稿を持つことができるため、ユーザーと投稿は1対多の関係にあると言えます。このような関連付けを行うことで、以下のようなことが簡単にできるようになります。

  • あるユーザーの投稿をすべて取得する
  • 投稿がどのユーザーによって作成されたかを知る
  • 新しい投稿をユーザーに紐づけて保存する

それでは、実際にモデルを作成して関連付けを行う方法を見ていきましょう。

関連付けのための準備

Userモデルの作成

まずは、ユーザー情報を保存するためのUserモデルを作成します。ターミナルで以下のコマンドを実行しましょう。

rails generate model User name:string

このコマンドを実行すると、以下のファイルが生成されます。

  1. マイグレーションファイル - データベースのテーブル構造を定義するファイル
  2. モデルファイル - モデルのクラスを定義するファイル

生成されたファイルを確認してみましょう。

マイグレーションファイル

# db/migrate/xxxxxx_create_users.rb
class CreateUsers < ActiveRecord::Migration[8.0]
  def change
    create_table :users do |t|
      t.string :name

      t.timestamps
    end
  end
end

このファイルは、usersテーブルを作成するための設計図のようなものです。t.string :nameという行は、nameという文字列型のカラムを作成することを指示しています。また、t.timestampscreated_atupdated_atという2つの日時カラムを自動的に追加します。

モデルファイル

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

現時点ではモデルファイルには特別な記述はありませんが、後ほど関連付けのためのコードを追加していきます。

マイグレーションの実行

モデルを作成したら、マイグレーションを実行してデータベースに実際にテーブルを作成します。

rails db:migrate

このコマンドを実行すると、usersテーブルがデータベースに作成されます。

Blogモデルの作成

次に、ユーザーが投稿するブログのためのBlogモデルを作成します。1対多の関係を表現するために、Blogモデルには「どのユーザーの投稿か」を示すカラムが必要です。

Railsでは、references型を使うことで簡単に外部キーを設定できます。以下のコマンドを実行しましょう。

rails generate model Blog content:text user:references

このコマンドでは、以下のことを指示しています。

  • contentというtext型のカラムを作成する
  • userへの参照(外部キー)を作成する

生成されたファイルを見てみましょう。

マイグレーションファイル

# db/migrate/xxxxxx_create_blogs.rb
class CreateBlogs < ActiveRecord::Migration[8.0]
  def change
    create_table :blogs do |t|
      t.text :content
      t.references :user, null: false, foreign_key: true

      t.timestamps
    end
  end
end

t.references :userという行に注目してください。これは以下のことを行います。

  1. user_idというカラムを作成する
  2. null: falseで、このカラムには必ず値が必要であることを指定する
  3. foreign_key: trueで、このカラムがusersテーブルのidカラムを参照する外部キーであることを指定する

このuser_idカラムが、ユーザーと投稿を紐づけるための重要な要素になります。例えば、IDが1のユーザーが投稿したブログの場合、そのブログのuser_idには1が保存されます。

モデルファイル

# app/models/blog.rb
class Blog < ApplicationRecord
  belongs_to :user
end

Blogモデルには、自動的にbelongs_to :userという行が追加されています。この行は、「ブログはユーザーに属している」という関係を表現しています。

Userモデルの更新

次に、Userモデルにも関連付けのコードを追加する必要があります。Userモデルを以下のように編集しましょう。

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

has_many :blogsという行を追加することで、「ユーザーは多くのブログを持っている」という関係を表現しています。

マイグレーションの実行

Blogモデルを作成したら、再びマイグレーションを実行してデータベースにblogsテーブルを作成します。

rails db:migrate

これで、usersテーブルとblogsテーブルが作成され、ユーザーと投稿を関連付けるための準備が整いました。

belongs_toとhas_manyの関係

belongs_toの役割

belongs_toは「〜に属する」という意味です。例えば、「ブログはユーザーに属する」という関係を表現するために、Blogモデルにbelongs_to :userと記述します。

class Blog < ApplicationRecord
  belongs_to :user
end

この設定により、ブログがどのユーザーに属しているかを示すuser_idカラムを参照して、関連するユーザーを取得できるようになります。

has_manyの役割

has_manyは「多くの〜を持つ」という意味です。例えば、「ユーザーは多くのブログを持つ」という関係を表現するために、Userモデルにhas_many :blogsと記述します。

class User < ApplicationRecord
  has_many :blogs
end

この設定により、ユーザーに関連する全てのブログを取得できるようになります。

両者の関係

この2つの設定を組み合わせることで、Railsは「ブログがどのユーザーに属しているか」を自動的に把握し、それに応じた便利なメソッドを提供してくれます。

belongs_tohas_manyは対になる関係です。1対多の関係では、「多」側のモデルにbelongs_toを、「1」側のモデルにhas_manyを設定します。

Railsコンソールで関連付けを試す

関連付けが正しく設定できたか確認するために、Railsコンソールを使って実際にデータを作成し、関連付けを試してみましょう。

Railsコンソールの起動

Railsコンソールを起動するには、ターミナルで以下のコマンドを実行します。

rails console

Railsコンソールは、Railsアプリケーションのコードやモデルを対話形式で扱える便利なツールです。実務でもデバッグやデータ確認によく使われますので、ぜひ使い方を覚えておきましょう。

ユーザーの作成

まず、新しいユーザーを作成します。

user = User.create(name: "Tomada")

createメソッドはデータベースにレコードを作成するメソッドです。ここでは、nameが「Tomada」のユーザーを作成しています。

なお、createメソッドはnewsaveを組み合わせたメソッドです。以下のように書くこともできます。

user = User.new(name: "Tomada")
user.save

どちらの方法でも結果は同じです。

ブログの作成

次に、先ほど作成したユーザーに紐づけてブログを作成します。ブログを作成する方法はいくつかあります。

方法1: user_idを直接指定する

Blog.create(content: "first blog", user_id: user.id)

この方法では、user_idに直接ユーザーのIDを指定します。

方法2: ユーザーの関連付けを通じて作成する

user.blogs.create(content: "second blog")

この方法では、関連付けを通じてブログを作成するため、自動的にuser_idが設定されます。

どちらの方法でも結果は同じですが、関連付けを通じて作成する方法の方がより簡潔で、Railsの関連付けの利点を活かせます。

関連付けの確認

では、作成したデータを使って関連付けが正しく機能しているか確認してみましょう。

ユーザーからブログを取得する

ユーザーに関連するブログを取得するには、blogsメソッドを使います。

user = User.first  # 最初のユーザーを取得
user.blogs         # ユーザーに関連するブログを取得

実行すると、以下のような結果が得られます。

#=>
[#<Blog:0x0000000109e71490
  id: 1,
  content: "first blog",
  user_id: 1,
  created_at: "2025-03-07 04:43:32.424183000 +0000",
  updated_at: "2025-03-07 04:43:32.424183000 +0000">,
 #<Blog:0x000000010a4719a8
  id: 2,
  content: "second blog",
  user_id: 1,
  created_at: "2025-03-07 04:43:39.990426000 +0000",
  updated_at: "2025-03-07 04:43:39.990426000 +0000">]

この結果から、ユーザーに関連する2つのブログが取得できていることがわかります。

ブログからユーザーを取得する

逆に、ブログからそのブログを投稿したユーザーを取得するには、userメソッドを使います。

blog = Blog.first  # 最初のブログを取得
blog.user          # ブログに関連するユーザーを取得

実行すると、以下のような結果が得られます。

#=>
#<User:0x000000010a337bc8
 id: 1,
 name: "Tomada",
 created_at: "2025-03-07 04:42:37.958969000 +0000",
 updated_at: "2025-03-07 04:42:37.958969000 +0000">

この結果から、ブログに関連するユーザーが取得できていることがわかります。

このように、belongs_tohas_manyを設定することで、関連するデータを簡単に取得できるようになります。

よくあるミスと対処法

関連付けを設定する際によくあるミスとその対処法を紹介します。

belongs_toをつけ忘れた場合

belongs_toをつけ忘れると、blog.userのようなメソッドが使えなくなります。以下のようなエラーが発生します。

NoMethodError: undefined method `user' for #<Blog:0x...>

対処法は、Blogモデルにbelongs_to :userを追加することです。

has_manyをつけ忘れた場合

has_manyをつけ忘れても、データベース上は問題なく動作しますが、user.blogsのようなメソッドが使えなくなります。以下のようなエラーが発生します。

NoMethodError: undefined method `blogs' for #<User:0x...>

対処法は、Userモデルにhas_many :blogsを追加することです。

マイグレーションを実行していない場合

マイグレーションを実行していないと、テーブルやカラムが作成されず、エラーが発生します。

対処法は、以下のコマンドでマイグレーションの状態を確認し、未実行のマイグレーションがあれば実行することです。

rails db:migrate:status  # マイグレーションの状態を確認
rails db:migrate         # 未実行のマイグレーションを実行

外部キー制約に違反した場合

null: falseforeign_key: trueの制約があるカラムに不正な値を設定しようとすると、エラーが発生します。

対処法は、参照先のモデル(この場合はUser)が存在することを確認し、正しいuser_idを設定することです。

まとめ

本章では、Railsにおける1対多の関連付けについて学びました。以下の内容を理解できたことと思います。

  • 1対多の関連付けとは、あるモデルが複数の別モデルと関連付けられる関係のこと
  • belongs_toは「〜に属する」、has_manyは「多くの〜を持つ」という関係を表現する
  • 関連付けを行うには、子モデル側に親モデルへの参照(外部キー)が必要
  • マイグレーションファイルのreferences型を使うと、簡単に外部キーを設定できる
  • Railsコンソールを使うと、関連付けの動作を確認できる

1対多の関連付けは、データベースの構造を設計する上で最も基本的かつ重要な概念の一つです。この関連付けを理解することで、より複雑なアプリケーションの開発に取り組む基盤ができました。

今後は別のモデルとの関連付けも応用していくことで、アプリに様々な機能を取り入れられるようになったかと思います。

Previous
layoutとCSSで見た目を整えよう