みなさん、こんにちは。
前回は、Active Recordモデルに書くバリデーションを確認しました。今回は、複数のモデル同士の関係を表す「アソシエーション」を取り上げましょう。
Railsアプリケーションでは、1つのモデルだけで完結することはあまりありません。たとえば、ユーザーが記事を書く、記事にコメントが付く、注文に明細がある、というように、データ同士には関係があります。アソシエーションは、その関係をRailsのモデルとして分かりやすく表現するための仕組みです。試験では、has_many、belongs_to、has_one などの基本的な意味や、外部キーとの関係が問われやすいところです。メソッド名とその意味を覚えるだけではなく、「どちらが相手を持っているのか」を意識することが重要です。
アソシエーションとは何か
アソシエーションは、モデル同士の関係を宣言する仕組みです。たとえば、1人のユーザーが複数の記事を持つ場合、User と Article の関係は次のように書けます。
class User < ApplicationRecord
has_many :articles
end
class Article < ApplicationRecord
belongs_to :user
endhas_many は「多くの相手を持つ」という意味です。User から見ると、1人のユーザーは複数の記事を持つので has_many :articles になります。
belongs_to は「相手に属する」という意味です。Article から見ると、1つの記事は1人のユーザーに属します。そのため Article 側には belongs_to :user を書きます。同じ関係でも、読む向きによって書き方が変わります。
外部キーとの関係
アソシエーションは、データベースの外部キーと深く関係しています。先ほどの User と Article の例では、articles テーブルに user_id カラムを持たせます。
articlesテーブルのイメージ
| id | title | user_id |
| 1 | Rails入門 | 5 |
| 2 | 試験対策 | 5 |
この user_id があることで、Article はどの User に属しているかを判断できます。belongs_to :user と書くと、通常は user_id という外部キーがあるものとして扱われます。
試験では、belongs_to を書いた側に外部キーが置かれる、と覚えておくと整理しやすいです。今回の例なら、Article が User に属しているので、articles テーブルに user_id があります。
関連先をメソッドのように扱う
アソシエーションを書くと、関連するデータをメソッドのように取り出せます。
user = User.find(5)
user.articles
article = Article.find(1)
article.useruser.articles と書くと、そのユーザーに紐づく記事の一覧を取得できます。SQLを毎回書かなくても、モデル同士の関係をRubyのコードとして読めるのがRailsらしいところです。ただし、内部ではデータベースへの問い合わせが行われています。短く書けるからといって、処理が行われないわけではありません。アソシエーションは、「関係をたどるための入口」だと理解するとよいでしょう。
has_one と has_many
has_many は複数の相手を持つ関係です。一方、has_one は1つだけ相手を持つ関係です。たとえば、User が1つの Profile を持つ場合は、次のように書けます。
class User < ApplicationRecord
has_one :profile
end
class Profile < ApplicationRecord
belongs_to :user
endこの場合も、外部キーは belongs_to 側に置かれます。つまり profiles テーブルに user_id が入ります。has_one と belongs_to はどちらも「1対1」に見えますが、外部キーを持つ側は belongs_to です。混乱しやすいのは、「1つだけだから両方 has_one でよい」と考えてしまう点です。どちらのテーブルが相手のidを持っているかを意識すると、関係を判断しやすくなります。
dependentの扱い
関連するデータを削除するときは、dependent オプションを使うことがあります。たとえば、ユーザーを削除したら、そのユーザーの記事も削除したい場合です。
class User < ApplicationRecord
has_many :articles, dependent: :destroy
enddependent: :destroy を指定すると、User が削除されるときに、関連する articles も destroy されます。コールバックも実行されるため、単純にデータベースから消すだけではありません。
なお、実務では、dependent は慎重に使いましょう。ユーザーを消したときに記事まで消してよいのかは、アプリケーションの設計によって変わります。コードを短くするだけでなく、データの意味を考えて判断します。
ネストしすぎない設計
アソシエーションを使うと、user.articles.comments のように関係を繋いでたどりたくなることがあります。しかし、関係を深くたどりすぎると、コードの見通しが悪くなります。ルーティングの回で扱ったネストリソースと同じで、モデルの関係も「何でも深くつなげればよい」わけではありません。まずは、has_many と belongs_to で基本的な1対多の関係を読めるようにしましょう。
模擬問題
問題1
1人のユーザーが複数の記事を持ち、1つの記事は1人のユーザーに属する場合、Article モデルに書くアソシエーションとして最も適切なものを1つ選びなさい。
has_many :usersbelongs_to :userhas_one :articlesbelongs_to :articles
正解:
2
解説:
Article から見ると、1つの記事は1人のユーザーに属します。そのため Article モデルには belongs_to :user を書きます。1は Article が複数のユーザーを持つ形です。3は単数と複数の関係が合っていません。4は belongs_to の相手が複数形になっています。
問題2
User has_many :articles、Article belongs_to :user の関係で、通常外部キーが置かれるテーブルとして最も適切なものを1つ選びなさい。
- users テーブルに article_id を置く
- articles テーブルに user_id を置く
- users テーブルに user_id を置く
- 外部キーはどちらにも不要である
正解:
2
解説:
belongs_to を書く側が、通常は外部キーを持ちます。この例では Article が User に属しているので、articles テーブルに user_id を置きます。1は向きが逆です。3は users テーブル自身のidと混同しています。4では、Railsが関連をたどるための情報が足りません。
問題3
Railsらしいアソシエーション設計として最も適切なものを1つ選びなさい。
- 関連データを削除するかどうかは dependent を使えば常に自動削除にする
- 外部キーの位置を考えず、すべてのモデルに has_many を書く
- データの意味を考え、必要な関係をモデルに宣言する
- 関連データは毎回SQL文字列をビューで組み立てて取得する
正解:
3
解説:
アソシエーションは、データ同士の関係をモデルで表すための仕組みです。必要な関係を宣言すると、コードも読みやすくなります。1は dependent の影響を軽く見すぎています。2は外部キーや関係の向きを無視しています。4はビューに責務を持たせすぎています。
まとめ
今回は、Railsのアソシエーションについて整理しました。アソシエーションは、モデル同士の関係を宣言し、関連するデータをRubyのメソッドのように扱えるようにする仕組みです。
試験では、has_many、belongs_to、has_one の意味を押さえておきましょう。特に、belongs_to 側に外部キーが置かれるという考え方は重要です。user.articles や article.user のように、どちらのモデルから見ているのかを意識すると理解しやすくなります。
実務では、アソシエーションはデータ設計そのものに関わります。関連を宣言すれば便利に使うことができますが、dependent による削除や、深すぎる関係のたどり方には注意が必要です。
次回は、テーブル構造を変更するためのマイグレーションを扱います。
Rails7認定ベーシック試験について
全国300か所で通年実施しています。詳細は以下をご覧ください。

また、450ページを超える教科書も安価に出版しています。学習にあたってご活用ください。詳細は以下をご覧ください。
Rails 7 技術者認定ベーシック試験公式教科書ベータ版
著者:小澤昌樹 発行:Rails技術者認定試験運営委員会
価格(税込):ペーパーバック版 2,497円 Kindle版 1250円
ページ数:471ページ


