🌟

Railsの中間テーブルでつまずいた話|自動で作られないレコードの作成

に公開

はじめに

こんにちは。
プログラミングスクールで Ruby on Rails を主に学習している、りゅうという者です。

現在は卒業制作として Web アプリを作成しています。
その中で今回、中間テーブル を使う際に「中間テーブルのレコードも自分で作成しなければならない」という点を学んだので、記事にまとめました。

⚠️ 注意事項

プログラミング初心者のため、内容に誤りがある可能性があります。
また、お使いの PC や開発環境によっては動作しない場合があります。

お気付きの点がありましたら、ぜひコメントなどで教えていただけると嬉しいです。
どうぞよろしくお願いいたします。

🧩 使用環境(バージョン情報)

ツール バージョン
Ruby 3.2.3
Rails 7.2.2.2
PostgreSQL 17.6
開発環境 Docker

記事の内容

投稿のレコードを一覧で表示するまでの流れになっています。
最初のコード

エラー

原因の特定

変更したコード

結果

最初のコード

以下のように書いて、@proverbレコードを作成と取得、画面の表示を行おうとしています

app/controllers/proverb_cotributor.rb
def create
    @proverb = current_user.proverbs.build(proverb_params)
    if @proverb.save
      redirect_to root_path, notice: "ことわざを登録しました。"
    else
      render :new, status: :unprocessable_entity
    end
end

def index
    @proverbs = Proverb.includes(proverb_contributors: :user)
                       .order(created_at: :desc)
end
app/views/proverbs/index.html.erb
<div class="bg-rose-100 h-full py-12 px-8">
  <div class="grid grid-cols-1 sm:grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-8">
    <% @proverbs.each do |proverb| %>
      <div class="bg-white rounded-xl shadow-md p-4 flex flex-col items-center justify-between">
        <!-- ユーザー情報 -->
        <% proverb.proverb_contributors.each do |proverb_contributor| %>
          <%= proverb_contributor.user.name %>
        <% end %>
        <!-- ことわざタイトル -->
        <div class="border rounded-xl px-4 py-3 text-center w-full font-medium text-gray-900">
          <%= proverb.title %>
        </div>
      </div>
    <% end %>
  </div>
</div>

(proverb_contributors: :user)
こう書くのは、proverb_contributors対userが多対1だからです

エラー

開発画面で確認するとユーザー名が表示されていません

原因の特定

コンソールで確認します

bash
myapp(dev)> p = Proverb.last
  Proverb Load (1.0ms)  SELECT "proverbs".* FROM "proverbs" ORDER BY "proverbs"."id" DESC LIMIT $1  [["LIMIT", 1]]
=> 
#<Proverb:0x0000ffffb65a47e8
...
myapp(dev)> p
=> 
#<Proverb:0x0000ffffb65a47e8
 id: 10,
 word1: "絵の具",
 word2: "星",
 title: "わかめの勇気、ライオンの心",
 meaning: "わかめの勇気、ライオンの心",
 example: "わかめの勇気、ライオンの心",
 status: "completed",
 room_id: nil,
 created_at: "2025-10-08 02:48:26.732776000 +0000",
 updated_at: "2025-10-08 02:48:26.732776000 +0000">
myapp(dev)> 
myapp(dev)> 
myapp(dev)> 
myapp(dev)> pcs = p.proverb_contributors
enum :role, {:solo=>0, :word_provider=>1, :proverb_maker=>2}
 (called from <class:ProverbContributor> at /myapp/app/models/proverb_contributor.rb:5)
  ProverbContributor Load (0.4ms)  SELECT "proverb_contributors".* FROM "proverb_contributors" WHERE "proverb_contributors"."proverb_id" = $1 /* loading for pp */ LIMIT $2  [["proverb_id", 10], ["LIMIT", 11]]
=> []

コンソールで確認すると中間テーブルがないことが確認できます

変更したコード

中間テーブルのレコードがないことで表示ができないことがわかったので、中間テーブルのレコードを作成するコードに変更していきます

app/controllers/proverb_cotributor.rbのcreateメソッドを変更していきます

app/controllers/proverb_cotributor.rb
def create
    # 失敗した時にロールバックするためにトランザクションを使用
    ActiveRecord::Base.transaction do
      @proverb = Proverb.new(proverb_params)
      @proverb.save!
      @proverb.proverb_contributors.create!(user: current_user, role: :solo)
    end

    redirect_to root_path, notice: "ことわざを登録しました。"
    rescue ActiveRecord::RecordInvalid
      render :new, status: :unprocessable_entity
  end

!はfalseではなく、例外を返します。
例外はtransactionブロック外まで伝播するので、
rescueでこの例外を受け取れます。

結果

コンソールで確認していきます

bash
myapp(dev)> p = Proverb.last
  Proverb Load (1.4ms)  SELECT "proverbs".* FROM "proverbs" ORDER BY "proverbs"."id" DESC LIMIT $1  [["LIMIT", 1]]
=> 
#<Proverb:0x0000ffffb7e02088
...
myapp(dev)> p
=> 
#<Proverb:0x0000ffffb7e02088
 id: 11,
 word1: "絵の具",
 word2: "トンビ",
 title: "わかめの勇気、ライオンの心",
 meaning: "わかめの勇気、ライオンの心",
 example: "わかめの勇気、ライオンの心",
 status: "completed",
 room_id: nil,
 created_at: "2025-10-08 02:58:38.566107000 +0000",
 updated_at: "2025-10-08 02:58:38.566107000 +0000">
myapp(dev)> pcs = p.proverb_contributors
  ProverbContributor Load (1.9ms)  SELECT "proverb_contributors".* FROM "proverb_contributors" WHERE "proverb_contributors"."proverb_id" = $1 /* loading for pp */ LIMIT $2  [["proverb_id", 11], ["LIMIT", 11]]
=> 
[#<ProverbContributor:0x0000ffffb6880ac0
...
myapp(dev)> pcs
  ProverbContributor Load (1.2ms)  SELECT "proverb_contributors".* FROM "proverb_contributors" WHERE "proverb_contributors"."proverb_id" = $1 /* loading for pp */ LIMIT $2  [["proverb_id", 11], ["LIMIT", 11]]
=> 
[#<ProverbContributor:0x0000ffffbcbc8358
  id: 2,
  user_id: 2,
  proverb_id: 11,
  created_at: "2025-10-08 02:58:38.588930000 +0000",
  updated_at: "2025-10-08 02:58:38.588930000 +0000",
  role: "solo">]

proverb_contributorという中間テーブルのレコードもしっかりできているのが確認できます

画面を見にいきます

しっかりユーザー名が表示されていますね。

まとめ

今回は中間テーブルのレコードも作成することを理解しました。考えれば当たり前なのですが、自動的にレコードが作られる訳ではないのですね。投稿とユーザーに中間テーブルを作ったのは初めてなので、勉強になりました。

間違えやもっと良い方法があったら教えて欲しいです。
よろしくお願いします🙇‍♀️

参考になった記事

https://qiita.com/south37/items/b2c81932756d2cd84d7d

https://qiita.com/hirotakasasaki/items/e0be0b3fd7b0eb350327

https://dev.to/meaganewaller/use-a-nested-dynamic-form-with-a-hasmany-through-association-in-rails-33hf

https://toshpit.com/rails-goodness-part1

最後に

noteで卒業制作の過程を書いています。
よかったら見てください!
https://note.com/ryu0121_it/n/n154649d7dce1

Discussion