どうした?
RailsでToDoアプリを開発しています。
ToDoリストを作成する時、
- 親モデル: Category
- 子モデル: Todo
を1つの動作で同時に作成する方法を調べたので、そのアウトプットです。
目標物の確認
今回作成するアプリはcategories/indexページにて新規投稿します。
新規投稿エリアにはcategory.title
とtodo.content
の記入欄を用意し、「投稿」ボタンを1つ押すだけで両方のモデルに対してcreate
されるようにします。
動作環境
$ ruby -v
=> ruby 3.0.2
$ rails -v
=> rails 6.1.7
方法
Model
本アプリでは親モデルにCategory
, 子モデルにTodo
を指定しています。
Categoryモデル
class Category < ApplicationRecord
# 親子関係の設定
has_many :todos
# controllerにてtodos_attributesが使えるようにする
accepts_nested_attributes_for :todos
end
親モデルではaccepts_nested_attributes_for :子モデル
を追加することで、コントローラにてパラメータ指定をする際に小モデルのカラムを指定ができるようになります。
Todoモデル
class Todo < ApplicationRecord
# 親子関係の設定
belongs_to :category
end
View
今回の機能を実装するのにポイントとなるのはfields_for
メソッドです。
fields_for
メソッドは親モデルのform_with
に対して子モデルのフォームをネストする形で使用します。
<%= form_with model: @category do |f| %> <%# 親モデルのフォームを作成 %>
<%= f.label :title, class: 'form-label' %>
<li><%= f.text_field :title %></li>
<%= f.fields_for :todos do |t| %> <%# fields_forで子モデルのフォームをネストする %>
<%= f.label :content, class: 'form-label' %>
<li><%= t.text_field :content %></li> <%# 変数tを使っていることに注意! %>
<% end %>
<%= f.submit '投稿' %>
<% end %>
Controller
class CategoriesController < ApplicationController
def index
# indexに関する内容
@categories = Category.all
# newに関する内容
@category = Category.new
@todo = @category.todos.build
end
def create
@category = Category.new(category_params)
if @category.save
redirect_to categories_path
else
render 'categories'
end
end
private
def category_params
params.require(:category).permit(:title, todos_attributes: [:id, :content, :_destroy])
end
end
ストロングパラメータでは、Todoモデルに渡す値をtodos_attributes
で設定しています。
_destroy
は、Todoモデルのフォームが空のときに空データが送信されないために追加しています。
createアクションではCategoryモデルに関してnew
とsave
メソッドをおこなうだけで、Todoモデルに対しても同様のメソッドがなされます。
ステップアップ
複数のTodo.contentを一括で作成する方法に関する記事も執筆する予定です。
参考