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

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

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

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

Ruby

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

Q&A

解決済

1回答

638閲覧

[rails5]パラメータでネストした日時情報をフォームオブジェクトに渡したい

zendendo

総合スコア43

Ruby on Rails 5

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

Ruby

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

0グッド

0クリップ

投稿2019/04/18 14:02

編集2019/05/04 02:36

前提・実現したいこと

ruby on railsで、フォームオブジェクトとfields_forを使って
一つのフォーム入力画面から複数の異なったモデルを更新したいと考えています。

ただ、パラメータでネストした(fields_forで子モデルに入力する)日時の情報を
FormObjectに渡して正しい日時情報に変換したい
のですが、やり方がわからず困っています。

現状のソースコード

ビュー側

html

1 <%= form_for @contract_master, as: :edit_dract_contract_form do |fb| %> 2 3 <div class="form-group"> 4 <%= fb.label :契約名, class: "label-inline" %> 5 <%= fb.text_field :name, class:'form-control' %> 6 </div> 7 <!-- 子モデルへの入力フォーム --> 8 <%= fb.fields_for :temp_cont_item_stock do |f2| %> 9 <div class="form-group"> 10 <%= f2.label :契約受付説明文, class: "label-inline" %><span class="badge badge-pill badge-danger">必須</span> 11 <%= f2.text_area :description, class:'form-control', rows:"5" %> 12 </div> 13 <div class="form-group"> 14 <%= f2.label :使用期限, class: "label-inline" %> 15 <%= f2.datetime_select :expiration_date, start_year: now.year, end_year: now.year + 1000, class:'form-control' %> 16 </div> 17 <% end %> 18<% end %>

コントローラでフォームオブジェクトに渡すストロングパラメータの設定

ruby

1 def edit_contract_master_params 2 params.require(:edit_dract_contract_form).permit(:name, temp_cont_item_stock: [:description, :expiration_date]) 3 end

生成されたストロングパラメータの内容

<ActionController::Parameters {"name"=>"テスト契約", "temp_cont_item_stock"=><ActionController::Parameters {"description"=>"契約説明文です", "expiration_date(1i)"=>"2021", "expiration_date(2i)"=>"4", "expiration_date(3i)"=>"18", "expiration_date(4i)"=>"22", "expiration_date(5i)"=>"40"} permitted: true>} permitted: true>

フォームオブジェクトに日時情報(expiration_date)を渡そうとすると5つに分割された状態になってしまいます。
このままでは日時情報をフォームオブジェクト側でうまく受け取ることができません。
そこで5つに分かれてしまった日時情報を一つにまとまった日時情報に変換し直そうとしたのですがうまくいきませんでした。

フォームオブジェクト側

ruby

1class EditDractContractForm 2 include ActiveModel::Model 3 attr_accessor :name, :temp_cont_item_stock, :expiration_date 4 5 #年・月・日・時・分に分割されて渡されている日時を一つの日時情報に自動変換する 6 def initialize(params = {}) 7 8 if params.is_a?(ActionController::Parameters) 9 [:expiration_date].each do |attribute| 10 datetime_parts = (1..5).map { |i| params.delete("#{attribute}(#{i}i)") } 11 params[attribute] = Time.zone.local(*datetime_parts) if datetime_parts.any? 12 end 13 end 14 super 15 end 16#以下省略

ちなみに、パラメータの日時情報がネストしていないときは、上記のinitializeメソッドで日時情報に変換してくれます。
おそらくパラメータ内のネストした日時情報をinitializeメソッド内で取得すればいいのですが、その方法がわからなくて困っています。

どうすれば、うまく変換できるでしょうか。
回答をよろしくお願いします。

###追記(最終的に成功したコード)
回答者からのアドバイスをもとに最終的に成功したコードをここに記します。
(コメント欄だと見ずらいのでここに記します)。

class EditDractContractForm include ActiveModel::Model attr_accessor :name, :temp_cont_item_stock, :expiration_date def initialize(attributes={})#attributesにはパラメータが入る。 if attributes.present? attrs = attributes.to_unsafe_h.dup#受け取ったストロングパラメータをハッシュ化 if attrs["temp_cont_item_stock"]["expiration_date(1i)"].present? #日時作成 attrs[:expiration_date] = DateTime.new( attrs["temp_cont_item_stock"]["expiration_date(1i)"].to_i, attrs["temp_cont_item_stock"]["expiration_date(2i)"].to_i, attrs["temp_cont_item_stock"]["expiration_date(3i)"].to_i, attrs["temp_cont_item_stock"]["expiration_date(4i)"].to_i, attrs["temp_cont_item_stock"]["expiration_date(5i)"].to_i ) end super(attrs) end end

これでフォームオブジェクト内なら、expiration_dateとすれば日時情報を取得できます。

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

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

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

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

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

退会済みユーザー

退会済みユーザー

2019/04/18 14:34

1. ネストしているときのパラメーター 2. 最終的にどのような結果になっていればよいのか などを書くと解決しやすいと思います。
guest

回答1

0

ベストアンサー

expiration_dateというのはDate型でしょうか?それともDateTime型でしょうか?

  • Date型なら

datetime_selectdate_selectにすればRailsがうまいこと対応してくれそうな気がします。

  • DateTime型なら

変換してあげて、superに渡して上げればいいかと

ruby

1class EditDractContractForm 2 include ActiveModel::Model 3 attr_accessor :name, :temp_cont_item_stock, :expiration_date 4 5 def initialize(attributes={}) 6 attrs = attributes.dup # dupしたほうがいいかはわかりませんが 7 8 9 if attrs.key?("expiration_date(1i)") 10 attrs[:expiration_date] = DateTime.new( 11 attrs.delete["expiration_date(1i)"].to_i, 12 attrs.delete["expiration_date(2i)"].to_i, 13 attrs.delete["expiration_date(3i)"].to_i, 14 attrs.delete["expiration_date(4i)"].to_i, 15 attrs.delete["expiration_date(5i)"].to_i 16 ) 17 end 18 19 super(attrs) 20 end

これで動けば良いですが。

投稿2019/04/22 09:58

odyu

総合スコア548

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

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

zendendo

2019/05/03 13:28

返信が遅くなってすみません。 odyuさん、回答ありがとうございます。 expiration_dateは、日時を記録するDatetime型です。 教えて頂いたコードを試してみたのですが・・・ attrs.key?("expiration_date(1i)")の部分が、情報が入っていてもfalseになってしまいます。 また、attrs.delete["expiration_date(1i)"].to_iでは ArgumentError: wrong number of arguments (given 0, expected 1) というエラーが発生してしまいます。
odyu

2019/05/03 16:50

試してもらいたいのが、 params.requireにexpiration_date(1i)などのキーを追加する attrs = attributes.dup の部分を attrs = attributes.to_unsafe_h.dup に変更する attrs.deleteの[]を()に変更する ()が正しいです。間違ってました。 試してみてもらえますか
zendendo

2019/05/04 01:35

ストロングパラメータに params.require(:edit_dract_contract_form).permit(:name, temp_cont_item_stock: [:description, :expiration_date, :expiration_date(1i)])という風にキーを追加してみたのですが、 SyntaxErrorになってしまいます。 また、attrs.delete["expiration_date(1i)"].to_iはなぜか結果が0になってしまいます。
odyu

2019/05/04 01:37

expiration_date(1i)はストリングじゃないとダメじゃないかな 'expiration_date(1i)'ですね
zendendo

2019/05/04 02:00

params.require(:edit_dract_contract_form).permit(:name, temp_cont_item_stock: [:description, :expiration_date, 'expiration_date(1i)'])でパラメータ側のエラーは解決しました。 attrs = attributes.to_unsafe_h.dupでハッシュ化されたパラメータには情報が入っているのですが、 attrs.key?("expiration_date(1i)")はfalseになってしまいます。 {"name"=>"テスト契約", "temp_cont_item_stock"=> {"expiration_date(1i)"=>"2019", "expiration_date(2i)"=>"5", "expiration_date(3i)"=>"19", "expiration_date(4i)"=>"13", "expiration_date(5i)"=>"43", "exhibition_quantity"=>"12"}}
odyu

2019/05/04 02:02

hashがネストしていたんですね。 temp_cont_item_stockを取り出して、attrに入れてあげればうまく行きそうですね。
zendendo

2019/05/04 02:27

最終的には以下のようなコードにすることで分割されていない正しい形の日時情報をフォームオブジェクト側で取得することができました。 度重なるアドバイスと素早い対応に感謝します。ありがとうございました。 フォームオブジェクト側の記述 ############################################## class EditDractContractForm include ActiveModel::Model attr_accessor :name, :temp_cont_item_stock, :expiration_date def initialize(attributes={}) binding.pry#デバック用 if attributes.present? attrs = attributes.to_unsafe_h.dup# dupしたほうがいいかはわかりませんが if attrs["temp_cont_item_stock"]["expiration_date(1i)"].present? attrs[:expiration_date] = DateTime.new( attrs["temp_cont_item_stock"]["expiration_date(1i)"].to_i, attrs["temp_cont_item_stock"]["expiration_date(2i)"].to_i, attrs["temp_cont_item_stock"]["expiration_date(3i)"].to_i, attrs["temp_cont_item_stock"]["expiration_date(4i)"].to_i, attrs["temp_cont_item_stock"]["expiration_date(5i)"].to_i ) end super(attrs) end end ###############################################
odyu

2019/05/04 02:48

良いと思います。解決して良かったです。おつかれさまでしたー
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.50%

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

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

質問する

関連した質問