質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.48%
Ruby on Rails 5

Ruby on Rails 5は、オープンソースのWebアプリケーションフレームワークです。「同じことを繰り返さない」というRailsの基本理念のもと、他のフレームワークより少ないコードで簡単に開発できるよう設計されています。

Ruby

Rubyはプログラミング言語のひとつで、オープンソース、オブジェクト指向のプログラミング開発に対応しています。

Q&A

0回答

2291閲覧

railsのフォームオブジェクトで編集に失敗しても入力内容を保持したままrenderしたい

zendendo

総合スコア43

Ruby on Rails 5

Ruby on Rails 5は、オープンソースのWebアプリケーションフレームワークです。「同じことを繰り返さない」というRailsの基本理念のもと、他のフレームワークより少ないコードで簡単に開発できるよう設計されています。

Ruby

Rubyはプログラミング言語のひとつで、オープンソース、オブジェクト指向のプログラミング開発に対応しています。

0グッド

0クリップ

投稿2019/05/05 01:42

編集2019/05/07 09:03

###実現したいことの概要
ruby on railsで複数の同一テーブルからのレコードを一括編集できる機能を作成したのですが、編集に失敗してedit画面に戻ったとき、入力欄に入力した内容を保持したままにしておきたいと考えています。

###現状
編集画面では、一つの「契約レコード」と契約が保有する複数の「契約詳細」を編集することができます。
編集更新作業は、一つのフォームオブジェクトに渡して行っています。
ビューではfield_forを使って、親モデル(契約)と複数の子モデル(契約詳細)の入力欄を表示させています。

コントローラ側

ruby

1class ContractMastersController < ApplicationController 2 def edit 3 #編集したいレコードを取得(フォームに表示する為に) 4 @contract_master = current_user.contract_masters.find(params[:id])#親モデルの1件の契約 5 @provide_detail_masters = @contract_master.provide_detail_masters#子モデルで複数ある契約詳細 6 @contract_master1 = EditDractContractForm.new#編集用のフォームオブジェクト 7 end 8 9 def update 10 @contract_master1 = EditDractContractForm.new(edit_contract_master_params) 11 @contract_master1.edit_contract_master = @contract_master 12 @contract_master1_result = @contract_master1.transaction_save#更新実行 13 #更新に成功した場合 14 if @contract_master1_result 15 flash[flash[:success] = '契約を下書き保存しました'] 16 redirect_to contract_masters_path(id: @contract_master.id) 17 #バリデーションエラー、もしくは例外が発生して更新失敗した場合、 18 elsif @contract_master1_result == 'validaiton_error' || @contract_master1_result == false 19 @contract_master.attributes = edit_contract_master_params2#親モデルへの入力内容を保持する 20 @provide_detail_masters = @contract_master.provide_detail_masters#子モデルを取得,renderしたときのフォームを表示させるために 21 ###問題個所、複数あるレコードのattributes 22 flash[:danger] = '契約の下書きに失敗しました。' 23 render 'edit' 24 end 25 end 26 private 27 #契約を編集するためのパラメータ 28 def edit_contract_master_params 29 params.require(:edit_dract_contract_form).permit(:name, :contract_text, povide_detail_masters: [:quantity, :ownership_form_master_id ]) 30 end 31 #契約への入力内容を保持するためだけのパラメータ(編集には使わない) 32 def edit_contract_master_params2 33 params.require(:edit_dract_contract_form).permit(:name, :contract_text) 34 end 35end 36 37

ビュー側

<%= form_for @contract_master, as: :edit_dract_contract_form do |fb| %> <% if @contract_master1.errors.any? %><!-- フォームオブジェクトでバリデーションエラーがあるかをチェック。ある場合は、エラーを表示する--> <h3>入力内容にエラーが<%= @contract_master1.errors.count %>件あります</h3> <ul> <% @contract_master1.errors.full_messages.each do |message| %><!-- フォームオブジェクト(契約下書きFO)でのエラーメッセージを取得して、表示する--> <li><%= message %></li> <% end %> </ul> <!-- 親モデルへの入力フォーム--> <div class="form-group"> <%= fb.label :契約名, class: "label-inline" %><span class="badge badge-pill badge-danger">必須</span> <%= fb.text_field :name, class:'form-control' %> </div> <div class="form-group"> <%= fb.label :契約本文, class: "label-inline" %><span class="badge badge-pill badge-danger">必須</span> <%= fb.text_area :contract_text, class:'form-control', rows:"5" %> </div> <h3>契約詳細編集</h3> <table class="table"> <thead> <tr> <th>アイテム名</th> <th>提供数</th> <th>提供形態</th> </tr> </thead> <tbody class="bulk-registration-form"> <!-- 子モデルであり複数ある入力フォーム--> <% @provide_detail_masters.each do |provide_detail| %><!-- 契約マスタが保有する複数の契約詳細マスタレコードの数だけ繰り返し処理--> <% item = ItemStock.find(provide_detail.item_stock_id) %><!-- 繰り返し入れ替わる詳細レコードのアイテムストックIDからアイテムレコードを取得する --> <%= fb.fields_for "povide_detail_masters[]", provide_detail do |f3| %><!-- 契約マスタの子モデルである提供詳細マスタへの入力フォーム--> <tr class="item"> <td> <%= provide_detail.item_stock.item_name %> </td> <td> <%= f3.number_field :quantity, min:0,max:999999999%> </td> <td> <%= f3.collection_select :ownership_form_master_id, OwnershipFormMaster.where(id: 4..6), :id, :name , {}, {class: 'form-control'} %> </td> </tr> <% end %><!-- 繰り返し作業f3のend(提供詳細マスタへの入力フォームのend)--> <% end %> </tbody> </table> <div class="text-center"> <%= fb.submit "下書き保存する", name: 'draft_save', class: "btn btn-lg btn-primary" %> </div> <% end %>

フォームオブジェクト側

class EditDractContractForm # include ActiveModel::Model #親モデル「契約」への入力内容 文字列としてそのまま渡される attr_accessor :name, :contract_text #子モデル「契約詳細」への入力内容、 複数のハッシュとして渡される attr_accessor :povide_detail_masters def transaction_save #更新処理、省略 end end

###問題点の推測

@contract_master1.povide_detail_mastersには、以下のようなハッシュで子モデルの入力内容が入っています。

ruby

1{"73"=>{"quantity"=>"50", "ownership_form_master_id"=>"6"}, "74"=>{"quantity"=>"50", "ownership_form_master_id"=>"6"}

@provide_detail_mastersに入っている複数の契約詳細レコードのattributesに渡せば、ビューにある複数の子モデルの入力欄に新しい入力内容を保持できるのではないかと考えたのですが、やり方がわからず困っています。

例えば、上記のハッシュをeach doで渡した場合は、最後の1件しか残りません。

#コントローラ側に追記する @provide_detail_masters2 = @contract_master.provide_detail_master#契約詳細取得 @input_provide_detail_masters = @contract_master1.povide_detail_masters#契約詳細への入力内容取得 @input_provide_detail_masters.each do |id, ipdm| @provide_detail_masters = @provide_detail_masters2.find(id) @provide_detail_masters.attributes = ipdm end

さらに、この場合ではビュー側の@provide_detail_masters.each do |provide_detail|にて、
undefined method `each' for #ProvideDetailMaster:0x007f4c0eaff6c0
エラーが発生してしまいます。

@provide_detail_mastersには複数の契約詳細レコードが入っており、なおかつ各レコードのattributesには新しい入力内容が入っている状態にしたいのですが、どうすればできるのでしょうか?

###追記
accepts_nested_attributes_forを使った場合のコードです。

モデル

class ContractMaster < ApplicationRecord #複数の契約マスタは、一人のユーザーに従属している belongs_to :user has_many :contracts has_many :provide_detail_masters #フォームにて子モデルを同時更新する為に追記 accepts_nested_attributes_for :provide_detail_masters, allow_destroy: true accepts_nested_attributes_for :temp_cont_item_stock, allow_destroy: true end

コントローラ

class ContractMastersController < ApplicationController def edit #編集したいレコードを取得(フォームに表示する為に) @contract_master = current_user.contract_masters.find(params[:id])#親モデルの1件の契約 @contract_master1 = EditDractContractForm.new#編集用のフォームオブジェクト end def update @contract_master1 = EditDractContractForm.new(edit_contract_master_params) @contract_master1.edit_contract_master = @contract_master @contract_master1_result = @contract_master1.transaction_save#更新実行 #更新に成功した場合 if @contract_master1_result flash[flash[:success] = '契約を下書き保存しました'] redirect_to contract_masters_path(id: @contract_master.id) #バリデーションエラー、もしくは例外が発生して更新失敗した場合、 elsif @contract_master1_result == 'validaiton_error' || @contract_master1_result == false @contract_master.attributes = edit_contract_master_params#入力内容を渡して保持している flash[:danger] = '契約の下書きに失敗しました。' render 'edit' end end private #契約を編集するためのパラメータ def edit_contract_master_params #accepts_nested_attributes_forを使った場合、 params.require(:edit_dract_contract_form).permit(:name, :contract_text, provide_detail_masters_attributes: [:quantity, :ownership_form_master_id, :id ]) end end

ビュー

<%= form_for @contract_master, as: :edit_dract_contract_form do |fb| %> <% if @contract_master1.errors.any? %><!-- フォームオブジェクトでバリデーションエラーがあるかをチェック。ある場合は、エラーを表示する--> <h3>入力内容にエラーが<%= @contract_master1.errors.count %>件あります</h3> <ul> <% @contract_master1.errors.full_messages.each do |message| %><!-- フォームオブジェクト(契約下書きFO)でのエラーメッセージを取得して、表示する--> <li><%= message %></li> <% end %> </ul> <!-- 親モデルへの入力フォーム--> <div class="form-group"> <%= fb.label :契約名, class: "label-inline" %><span class="badge badge-pill badge-danger">必須</span> <%= fb.text_field :name, class:'form-control' %> </div> <div class="form-group"> <%= fb.label :契約本文, class: "label-inline" %><span class="badge badge-pill badge-danger">必須</span> <%= fb.text_area :contract_text, class:'form-control', rows:"5" %> </div> <h3>契約詳細編集</h3> <table class="table"> <thead> <tr> <th>アイテム名</th> <th>提供数</th> <th>提供形態</th> </tr> </thead> <tbody class="bulk-registration-form"> <!-- 子モデルであり複数ある入力フォーム--> <%= fb.fields_for provide_detail_masters do |f3| %><!-- 契約マスタの子モデルである提供詳細マスタへの入力フォーム--> <tr class="item"> <td> </td> <td> <%= f3.number_field :quantity, min:0,max:999999999%> </td> <td> <%= f3.collection_select :ownership_form_master_id, OwnershipFormMaster.where(id: 4..6), :id, :name , {}, {class: 'form-control'} %> </td> </tr> <% end %><!-- 繰り返し作業f3のend(提供詳細マスタへの入力フォームのend)--> <% end %> </tbody> </table> <div class="text-center"> <%= fb.submit "下書き保存する", name: 'draft_save', class: "btn btn-lg btn-primary" %> </div> <% end %>

これで、フォームオブジェクトで更新が失敗してedit画面にrenderしても入力内容を保持することができました。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

siruku6

2019/05/06 03:58 編集

親子モデルの同時編集方法を修正する必要がありそうです。 コチラを参考に修正してみてエラーが出たりするようであれば再度追記いただければなと思いました https://qiita.com/shizuma/items/5bfcf6c1e855b959e135 ポイントは、controllerにおいて @contract_master @provide_detail_masters の二つを用意するのではなく、 @contract_master だけを用意してviewに渡すことです。(edit, update 両方において) そうすれば、@contract_masterだけで@provide_detail_mastersの情報もviewで取得できるようになります。 (@provide_detail_mastersはいらないです)
zendendo

2019/05/07 09:06 編集

siruku6さん、ありがとうございます。 accepts_nested_attributes_forを使ってみたところ、問題を解決することができました。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

まだ回答がついていません

会員登録して回答してみよう

アカウントをお持ちの方は

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.48%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問