accepts_nested_attributes_forを使った複数の子レコード同時作成、同時更新モッグを作る
手順
- Rails新規アプリ作成
- rails g scaffoldで親と子を作る
- アソシエーション(accepts_nested_attributes_for)
- view修正(fields_for)
- controller実装(attributes)
- 完成
rails新規アプリ作成
今回は一行のコマンドでrails new
からdb:create
までやりたいと思います。
railsのバージョンは5.2.3でデータペースはpostgresqlを使用します。
rails _5.2.3_ new sample-app -d postgresql && cd sample-app && bundle install --path vendor/bundle && rails db:create
rails g scaffoldで親と子を作る
まずはscaffoldで不要ファイルが生成されないように設定を書きます。
/config/application.rb
require_relative 'boot'
require 'rails/all'
# Require the gems listed in Gemfile, including any gems
# you've limited to :test, :development, or :production.
Bundler.require(*Rails.groups)
module SampleApp
class Application < Rails::Application
## ここから追加
config.generators do |g|
g.javascripts false
g.helper false
g.test_framework false
end
## ここまで
end
end
これも一行で親と子のDBを作っちゃいます。
$ rails g scaffold parent name:string age:integer && rails g model child name:string age:integer parent_id:integer && rails db:migrate
アソシエーション
それぞれのmodelにアソシエーションを追加して
親
parent.rb
class Parent < ApplicationRecord
has_many :childs
accepts_nested_attributes_for :childs, reject_if: :reject_blank_child, allow_destroy: true
def reject_blank_child(attributes)
exists = attributes[:id].present?
empty = attributes[:name].blank?
attributes.merge!(_destroy: 1) if exists && empty
!exists && empty
end
end
子
child.rb
class Child < ApplicationRecord
belongs_to :parent ,optional: true
end
view修正(fields_for)
views/parents/_form.html.erb
<%= form_for(parent) do |form| %>
<% if parent.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(parent.errors.count, "error") %> prohibited this parent from being saved:</h2>
<ul>
<% parent.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= form.label :parent_name %>
<%= form.text_field :name %>
</div>
<div class="field">
<%= form.label :parent_name %>
<%= form.number_field :age %>
</div>
## ここから追加
<hr>
<% n = 0 %>
<%= form.fields_for :childs do |f| %>
<%= "子供数#{n += 1}" %>
<div class="field">
<%= f.label :child_name %>
<%= f.text_field :name %>
</div>
<div class="field">
<%= f.label :child_age %>
<%= f.number_field :age %>
</div>
<% end %>
## ここまで
<div class="actions">
<%= form.submit %>
</div>
<% end %>
controller実装
app/controllers/properties_controller.rb
class ParentsController < ApplicationController
before_action :set_parent, only: [:show, :edit, :update, :destroy]
def index
@parents = Parent.all
end
def show
end
def new
@parent = Parent.new
5.times { @parent.childs.build } ##追記
end
def edit
end
def create
@parent = Parent.new(parent_params)
respond_to do |format|
if @parent.save
format.html { redirect_to @parent, notice: 'parent was successfully created.' }
format.json { render :show, status: :created, location: @parent }
else
format.html { render :new }
format.json { render json: @parent.errors, status: :unprocessable_entity }
end
end
end
def update
respond_to do |format|
if @parent.update(parent_params)
format.html { redirect_to @parent, notice: 'parent was successfully updated.' }
format.json { render :show, status: :ok, location: @parent }
else
format.html { render :edit }
format.json { render json: @parent.errors, status: :unprocessable_entity }
end
end
end
def destroy
@parent.destroy
respond_to do |format|
format.html { redirect_to properties_url, notice: 'parent was successfully destroyed.' }
format.json { head :no_content }
end
end
private
def set_parent
@parent = parent.find(params[:id])
end
def parent_params
params.require(:parent).permit(:name, :age, childs_attributes: [:name, :age, :_destroy, :id]) ##修正
end
end
$ rails db:migrate && rails s
以上です!お疲れ様でした!