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

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

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

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

Q&A

解決済

3回答

701閲覧

ネストしたモデルのフォームに入力した値を計算して、DBに登録したい

kawa0tatsu

総合スコア7

Ruby on Rails

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

0グッド

0クリップ

投稿2020/04/08 17:36

編集2020/04/12 14:33

前提・実現したいこと

楽曲の情報を登録するアプリを作っています。任意の楽曲の演奏時間を登録する機能を実装しようとしています。曲は複数の楽章から構成されます。

その際に入力した分と秒を楽章別に計算して秒に変換し、DBに登録したいです。form_withとfields_forを使います。

発生している問題・エラーメッセージ

曲の情報は親モデル(Post)、演奏そのものの情報は子モデル(Recording)、演奏時間の情報は孫モデル(Duration)の管轄です。

子モデルのフォームで分(min)と秒(sec)を登録し、それらを計算して秒(duration)に変換して、なおかつそれを孫モデルに格納する流れがわかりません。どうすればよいでしょうか。

Started POST "/posts/2/recordings" for ::1 Processing by RecordingsController#create as JS   Parameters: {"authenticity_token"=>"N3NSB14zOEEgMKWfWwtVxnHN3PCyNpO6zwJHuSiadQvOfHTeP9RBofItGZeWDPc407Z/HyfSXpCDnt7q KSWHQw==",  "recording"=>{"composer"=>"Mozart", "title"=>"Symphony No.40", "conductor"=>"test", "orchestra"=>"test", "durations_attributes"=>{    "0"=>{"mov"=>"1", "min"=>"1", "sec"=>"1"},     "1"=>{"mov"=>"2", "min"=>"2", "sec"=>"2"},     "2"=>{"mov"=>"3", "min"=>"3", "sec"=>"3"},     "3"=>{"mov"=>"4", "min"=>"4", "sec"=>"4"}    }, "year"=>"2000", "label"=>"test", "review"=>"test", "post_id"=>"2"},  "commit"=>"Create", "post_id"=>"2"} Completed 500 Internal Server Error in 4ms (ActiveRecord: 0.0ms | Allocations: 3104) NoMethodError (undefined method `set_duration' for nil:NilClass): app/controllers/recordings_controller.rb:19:in `recording_params_with_duration' app/controllers/recordings_controller.rb:12:in `create'

該当のソースコード

Ruby

1 2コントローラ(Recording) 3class RecordingsController < ApplicationController 4 5  def index 6  end 7 8  def new 9      @recording = Post.find(params[:post_id]).recordings.new 10      @recording.durations.build 11  end 12 13  def create 14      @recording = Recording.new(recording_params_with_duration) 15      @recording.save 16      redirect_to('/posts/') 17  end 18 19  def recording_params_with_duration 20    recording_params.reverse_merge(@durations.set_duration) 21  end 22 23  def recording_params 24        params.require(:recording).permit( 25     (中略)       26     durations_attributes: [:mov, :min, :sec, :duration] 27        ) 28  end 29 30コントローラ(duration) 31class DurationsController < ApplicationController 32 33end 34 35 36 37モデル 38 39Post(親モデル) 40class Post < ApplicationRecord 41 has_many :recordings 42 has_many :post_instruments 43 has_many :instruments, :through => :post_instruments 44 accepts_nested_attributes_for :recordings, {allow_destroy: true} 45end 46 47Recording(子モデル) 48class Recording < ApplicationRecord 49  belongs_to :post 50  has_many :durations 51  accepts_nested_attributes_for :durations 52end 53 54Duration(孫モデル) 55class Duration < ApplicationRecord 56  belongs_to :recording 57 58  def duration 59    min * 60 +  sec 60  end 61 62  def set_duration 63    {:duration => duration } 64  end 65end 66 67 68ビュー 69<%= form_with model: @recording, url: post_recordings_path do |f| %> 70 71  <% for i in 1..@recording.post.mov do %> 72    <%= f.fields_for :durations do |dur| %> 73      <%= dur.number_field :mov, value: "#{i}" %><br> 74      <%= dur.label :"mov#{i}min" %> 75      <%= dur.number_field :min,  min: 0 %><br> 76      <%= dur.label :"mov#{i}sec" %> 77      <%= dur.number_field :sec, min: 0, max: 59 %><br> 78    <% end %> 79  <% end %> 80 81データベース 82 83CREATE TABLE IF NOT EXISTS "recordings" ("id" integer NOT NULL PRIMARY KEY, (中略)CONSTRAINT "fk_rails_47949a0700" 84FOREIGN KEY ("post_id") 85  REFERENCES "posts" ("id") 86CREATE TABLE IF NOT EXISTS "durations" ("id" integer NOT NULL PRIMARY KEY,"min" integer DEFAULT NULL, "sec" integer DEFAULT NULL, "mov" integer DEFAULT NULL, "recording_id" integer DEFAULT NULL, "duration" integer, CONSTRAINT "fk_rails_4bba954102" 87FOREIGN KEY ("recording_id") 88  REFERENCES "recordings" ("id") 89

試したこと

マージを使ってパラメータをマージさせる方法を載せているウェブサイト、あるいは計算をする方法を載せているウェブサイトは複数あり、それを模倣してやってみました(例:https://qiita.com/miyzawa/items/18bb8d88ad004cbc439c)。しかしそれは同一モデル内での話で、親子モデル間でそれをすることができませんでした。

コントローラやモデルにおけるメソッドの間の受け渡しについての理解が足りないのではないかと思っております

補足情報(FW/ツールのバージョンなど)

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

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

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

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

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

guest

回答3

0

自己解決

結局、「そもそもDBに、2つの数値を計算した結果を格納する必要はない」ということに気づき、結局分と秒だけDBに登録して、ウェブページに表示させるときに秒に変換して、それを再び分:秒で表示することにしました。

durationモデルに以下の自作関数を作って(stringで入力したので、to_iでintegerに変換しないとエラーとなります)

rails

1 def total_seconds 2 min.to_i * 60 + sec.to_i 3 end

recordingコントローラに以下のことを書いたら望んでいたものが表示されるようになりました(別の問題が発生してはいますが、それは別の機会に書きます)。

rails

1 <% recording.durations.each do |duration| %> 2 Mov<%= duration.mov %> <%=Time.at(duration.total_seconds).utc.strftime("%M:%S") %>/ 3 <% end %>

そもそも自分の実力では最初に考えていたやり方自体が無理だということでした。
もちろん、2つの入力項目を計算した上でDBに別のカラムで格納する方法がある場合はお教えください。

ヒントをくれたりエラー理由を指摘してくださったお二方には感謝いたします。

投稿2020/04/13 06:27

kawa0tatsu

総合スコア7

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

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

0

def create @recording = Recording.new(recording_params_with_duration) で呼んでいる recording_params_with_duration にて
recording_params.reverse_merge(@durations.set_duration)
と @durations を使っていますが、この時点では @durations は未定義なのでこのエラーとなります。
どういう情報を reverse_merge したいのでしょう。

及び、質問の内容からして、孫modelのcodeも載せてください

投稿2020/04/09 07:13

winterboum

総合スコア23329

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

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

kawa0tatsu

2020/04/09 10:29

この時点では @durationsは未定義なのでこのエラーとなります。> durationsは、recordingコントローラのnewメソッドですでに定義されてるものだと思い込んでました。 どういう情報を reverse_mergeしたいのでしょう。> durationモデルのdurationメソッドでminとsecを計算したものを、set_durationメソッドでパラメータにし、それををmergeでくっつければ良いと思ってました。 恥ずかしながらmergeとreverse_mergeの違い自体よくわかっていなかったので、出来るのなら普通のmergeでいいです。 及び、質問の内容からして、孫modelのcodeも載せてください> 孫モデルはdurationだと思ってましたが、親モデルのpostのことでしょうか。一応追加しておきました。
guest

0

小モデルというか、プログラム的な考え方をすると二次配列による集計方法ですよね。

https://ja.stackoverflow.com/questions/56121/ruby-2%E6%AC%A1%E5%85%83%E9%85%8D%E5%88%97%E5%86%85%E3%81%AE%E8%A8%88%E7%AE%97%E6%96%B9%E6%B3%95

投稿2020/04/08 18:24

mackintosh

総合スコア228

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

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

kawa0tatsu

2020/04/13 07:45

ヒントありがとうございました。もっと勉強して、いつかその方向でできるようにしたいです。
guest

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

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

ただいまの回答率
85.48%

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

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

質問する

関連した質問