【Rails】ネストしたモデルに対するform_withの書き方

例えば以下のように、一つの投稿(post)の中にさらに複数の投稿(item)を追加できるようにしたい場合、

そんな時は、idなどのパラメータの受け渡しの都合上、ルーティングを以下のようにネスト(nest)して親子関係にした方が実装しやすいかと思います。

resources :posts do
  resources :items
end
        Prefix Verb   URI Pattern                                 Controller#Action
         posts GET    /posts(.:format)                            posts#index
               POST   /posts(.:format)                            posts#create
      new_post GET    /posts/new(.:format)                        posts#new
     edit_post GET    /posts/:id/edit(.:format)                   posts#edit
          post GET    /posts/:id(.:format)                        posts#show
               PATCH  /posts/:id(.:format)                        posts#update
               PUT    /posts/:id(.:format)                        posts#update
               DELETE /posts/:id(.:format)                        posts#destroy
    post_items GET    /posts/:post_id/items(.:format)             items#index
               POST   /posts/:post_id/items(.:format)             items#create
 new_post_item GET    /posts/:post_id/items/new(.:format)         items#new
edit_post_item GET    /posts/:post_id/items/:id/edit(.:format)    items#edit
     post_item GET    /posts/:post_id/items/:id(.:format)         items#show
               PATCH  /posts/:post_id/items/:id(.:format)         items#update
               PUT    /posts/:post_id/items/:id(.:format)         items#update
               DELETE /posts/:post_id/items/:id(.:format)         items#destroy

そこで、ルーティングが親子関係(親モデル:post、子モデル:item)にある場合において、子モデルに対するform_withの書き方について書き残しておこうと思います。

目次

前提条件

  • 2つのモデルが存在する(postモデルとitemモデル)
  • postモデルはitemモデルに対して「1対多」の関係にある
Rails.application.routes.draw do
  ・・・
  resources :posts do
    resources :items
  end
end
# app/models/post.rb
class Post < ApplicationRecord
  has_many :items, dependent: :destroy
end

# app/models/item.rb
class Item < ApplicationRecord
  belongs_to :post
end

ネストした子モデルに対するform_withの書き方

ネストした子モデル(item)でform_withを書く場合、通常と書き方が少し異なります。

結論から言うと、以下のようにitemモデルのフォーム(form_with)に複数のインスタンスを渡せばOKです。

# モデルのインスタンスを複数(@post, @item)渡す
<%= form_with(model: [@post, @item], local: true) do |f| %>
 ・・・
<% end %>

ネストした子モデル(item)に通常通りform_withを書くと起こるエラー

今回の場合、ネストした子モデル(item)の入力フォーム画面を作成する時、

<%= form_with(model: @item, local: true) do |f| %>
 ・・・
<% end %>

itemモデルだからmodel: @itemだな、といつも通りにform_withを書てしまいがちですが、そうすると以下のようなエラーが起こります。

NoMethodError in …
undefined method `items_path` for …

どうやら、items_pathが定義されていませんよ、と言うことらしい。

通常、ルーティングの設定で以下のようにitemモデル(itemsコントローラー)に対してresoucesを用いた場合、

resources :items

itemモデルのindexへのパスはitems_pathとなりますが、今回の場合はitemモデルのresoucesはネストされているため、itemモデルへのindexへのパスが以下のようにpost_items_pathと変更されます。

# 通常の場合
items_path GET         /items(.:format)                 items#index

# itemが子モデルの場合(親モデルはpost)
post_items_path GET    /posts/:post_id/items(.:format)  items#index

そのため、form_withのモデルを@itemとした場合、

<%= form_with(model: @item, local: true) do |f| %>
 ・・・
<% end %>

入力内容を送信する際は、自動的にitems_pathに対してPOSTメソッドで送信しようとします。

しかし、今回はネストしたことによりitemモデルのindexへのパスはすでにitems_pathからpost_items_pathへと変更されているため、form_withを上記のように書くと「items_pathが見つかりませんよ!」と怒られてしまうのです。

解決策

以下のように、子モデル(item)のform_withに、親子関係にあたるモデルのインスタンスを全て入れることで解決します。

# モデルのインスタンスを複数(@post, @item)渡す
<%= form_with(model: [@post, @item], local: true) do |f| %>
 ・・・
<% end %>

上記のように書くことで、送信先のurlであるpost_items_pathを自動的に生成してくれます。

また、親モデル(post)から見た子モデル(item)への入力フォームへのパスは以下のように指定します。

・・・

<div class="mb-3">
>> <%= link_to "アイテムを追加する", new_post_item_path(@post) %>
</div>

以上です。

関連記事

あわせて読みたい
【Rails】ネストしたルーティングのURLのidが入れ替わる問題への対処法 例えば、postモデルを親としてitemモデルのルーティングを以下のようにネストした場合、 resources :posts do resources :items end showページ、およびeditページを表...

参考記事

Just do IT
Rails ネストされたルートに対する form_with の書き方 - Just do IT やりたいこと 前提 結論 環境 前準備 ログを表示する ログ作成 ログを編集する ログを削除する ルーティングを整理 削除 フォームをパーシャル化 リポジトリ 感想 関連 や...
Qiita
ネストしているモデルに対するform_withの書き方 - Qiita #ネストしているモデルに対するform_withの書き方ネスト関係にある投稿記事(Postモデル)とコメント(Commentモデル)でcommentを投稿するformファイルでエラーが発生した際の...
よかったらシェアしてね!
  • URLをコピーしました!
  • URLをコピーしました!

この記事を書いた人

愛知の34歳。無職で暇になり始めたプログラミング(Ruby on Rails)の忘備録をまとめたブログです。最近は別にやりたいことができたのでプログラミングほぼやっていません。気が向いたらまた再開するかも。僕の日常はメインブログの方で更新しています。

コメント

コメントする

目次