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

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

ただいまの
回答率

91.36%

  • Ruby

    5168questions

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

  • Ruby on Rails 5

    77questions

親モデルと同時に子モデルに複数のレコードを作成したい

解決済

回答 2

投稿 2017/11/29 17:56 ・編集 2017/12/03 08:48

  • 評価
  • クリップ 0
  • VIEW 149

zendendo

score 12

前提・実現したいこと

ruby on railsで、取引アカウント同士の取引記録を実装しています。

入力フォームで、ユーザーが「取引相手の口座番号」と「取引額」の2項目を入力するだけで、
取引モデル(親モデル)で1件のレコードが作成されると同時に、 1:多として関連付けを施してある入出金モデル(子モデル)に複数のレコードを自動的に作成したいと思っています。
作成するレコードは、
「出金(支払)」のレコードと
「入金(受取)」のレコードの2件です。
図にするとこんな感じです。
![イメージ説明

これを実現しようとしたのですが2点ほど問題が発生して困っています。
1つ目は、
undefined method `transaction_type=' for #<DepositsAndWithdrawal::ActiveRecord_Associations_CollectionProxy:0x007f189cc329c0> というエラーが発生してしまって困っています。

2つ目は、入出金モデルの出金レコードの取引カラムの数値をマイナス数値として記録されないことに、 困っています。(入力フォームから渡されたパラメータの数値をマイナス数値に変換する方法がわからなくて困っています)

どうすれば、これらの問題を解消できるか教えて頂ければ幸いです。よろしくお願いします。

以下が現状のコードになります。

現状のコード

AccountTransaction(取引)モデルは、多数のdeposits_and_withdrawals(入出金)モデルを持つという 1:多の関連付けを施してあります。

class AccountTransaction < ApplicationRecord

  belongs_to :withdrawal, class_name: 'BasicIncomeAccount', :foreign_key => 'withdrawal_account_id'
  belongs_to :deposit, class_name: 'BasicIncomeAccount', :foreign_key => 'deposit_account_id'
#取引モデルは、入出金モデルを多数保有している
  has_many :deposits_and_withdrawals

end
class DepositsAndWithdrawal < ApplicationRecord
#入出金モデルは、一つの取引モデルに保有されている
    belongs_to :account_transaction

    belongs_to :basic_income_account

end

取引入力画面です。 app/views/account_transactions.html.erb

<%= form_for @account_transaction do |f|%>
 <% if @account_transaction.errors.any? %>
  <h3>入力内容にエラーが<%= @account_transaction.errors.count %>件あります</h3>
  <ul>
      <% @account_transaction.errors.full_messages.each do |message| %>
      <li><%= message %></li>
      <% end %>
  </ul>
 <% end %>
<div class="field">
  <%= f.label :送金相手の口座番号 %>
  <%= f.text_field :deposit_account_id %>
<%#  <%= f.number_field :deposit_account_id %>
  <%= f.label :送金額 %>
  <%= f.number_field :amount %> 
  <%= f.submit"送金確認" %>
</div>
<div>
<%= @account_transaction.errors.to_yaml %>
</div>
<% end %>

AccountTransactionのコントローラには、取引モデルへの記録と同時に、 入出金モデルにもレコードを作成するようにbuildメソッドを使い以下のように記述をしました。 しかし、 入力フォームで、取引を入力してsubmitボタンを押すと、 NoMethodError in AccountTransactionsController#create undefined method `transaction_type=' for #<DepositsAndWithdrawal::ActiveRecord_Associations_CollectionProxy:0x007f189cc329c0>
というエラーになってしまいます。

class AccountTransactionsController < ApplicationController

  def create
#以下5行のコードは、取引モデルに記録するためのコード
    @account_transaction = AccountTransaction.new(
      withdrawal_account_id: current_user.basic_income_account.id,
      deposit_account_id: BasicIncomeAccount.find_by(account_number: params[:account_transaction][:deposit_account_id]).id,
      amount: params[:account_transaction][:amount]
      )
    ##取引モデルのレコード作成と同時に入出金モデルにも関連付けしたレコードを作成する為のコード
  #以下5行は出金側のレコードを作成する
    @account_transaction.deposits_and_withdrawals.build
    @account_transaction.deposits_and_withdrawals.transaction_type = '出金'
    @account_transaction.deposits_and_withdrawals.basic_income_account_id = current_user.basic_income_account.id
    @account_transaction.deposits_and_withdrawals.amount = -params[:account_transaction][:amount]#ここの渡された数値をマイナス数値にしたい
    @account_transaction.save
  #以下5行は入金側のレコードを作成する
    @account_transaction.deposits_and_withdrawals.build
    @account_transaction.deposits_and_withdrawals.transaction_type = "入金"
    @account_transaction.deposits_and_withdrawals.basic_income_account_id = BasicIncomeAccount.find_by(account_number: params[:account_transaction][:deposit_account_id]).id
    @account_transaction.deposits_and_withdrawals.amount = params[:account_transaction][:amount]
    @account_transaction.save

###@account_transactionを保存し、保存できたらホーム画面(root_path)へ、失敗なら取引入力画面(new)へ飛ぶ。
    if @account_transaction.save
    redirect_to root_path
    else
      render "new"
    end
  end

  def account_transaction_params
  params.require(:account_transaction).permit(:deposit_account_id, :amount)
  end
end

ここまで読んでいただきありがとうございました。
もし、足りない部分を指摘して頂ければ追記します。

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

Rails 5.1.3
ruby 2.4.1

追記:アドバイスを受けて修正したコード

AccountTransactionのコントローラファイル。

class AccountTransactionsController < ApplicationController


  #取引を作成する 
  def new
    @account_transaction = AccountTransaction.new
  end

  #取引を作成して記録する
  def create
    @account_transaction = AccountTransaction.new(
      withdrawal_account_id: current_user.basic_income_account.id,
      deposit_account_id: BasicIncomeAccount.find_by(account_number: params[:account_transaction][:deposit_account_id]).id,
      amount: params[:account_transaction][:amount]
      )
#####################修正中箇所#############################
    #出金側
    @deposits_and_withdrawal1 = DepositsAndWithdrawal.new
    @deposits_and_withdrawal1.transaction_type = '出金'
    @deposits_and_withdrawal1.basic_income_account_id = current_user.basic_income_account.id
    @deposits_and_withdrawal1.amount = -1 * params[:account_transaction][:amount].to_f
    @account_transaction.deposits_and_withdrawals << @deposits_and_withdrawal1

    #入金側
    @deposits_and_withdrawal2 = DepositsAndWithdrawal.new
    @deposits_and_withdrawal2.transaction_type = '出金'
    @deposits_and_withdrawal2.basic_income_account_id = BasicIncomeAccount.find_by(account_number: params[:account_transaction][:deposit_account_id]).id,
    @deposits_and_withdrawal2.amount = params[:account_transaction][:amount]
    @account_transaction.deposits_and_withdrawals << @deposits_and_withdrawal2
#################修正中箇所はここまで######################
###@account_transactionを保存し、保存できたらホーム画面(root_path)へ、失敗なら取引入力画面(new)へ飛ぶ。
    if @account_transaction.save
    redirect_to root_path
    else
      render "new"
    end
  end

  def account_transaction_params
  params.require(:account_transaction).permit(:deposit_account_id, :amount)
  end
end
  • 気になる質問をクリップする

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

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

    クリップを取り消します

  • 良い質問の評価を上げる

    以下のような質問は評価を上げましょう

    • 質問内容が明確
    • 自分も答えを知りたい
    • 質問者以外のユーザにも役立つ

    評価が高い質問は、TOPページの「注目」タブのフィードに表示されやすくなります。

    質問の評価を上げたことを取り消します

  • 評価を下げられる数の上限に達しました

    評価を下げることができません

    • 1日5回まで評価を下げられます
    • 1日に1ユーザに対して2回まで評価を下げられます

    質問の評価を下げる

    teratailでは下記のような質問を「具体的に困っていることがない質問」、「サイトポリシーに違反する質問」と定義し、推奨していません。

    • プログラミングに関係のない質問
    • やってほしいことだけを記載した丸投げの質問
    • 問題・課題が含まれていない質問
    • 意図的に内容が抹消された質問
    • 広告と受け取られるような投稿

    評価が下がると、TOPページの「アクティブ」「注目」タブのフィードに表示されにくくなります。

    質問の評価を下げたことを取り消します

    この機能は開放されていません

    評価を下げる条件を満たしてません

    評価を下げる理由を選択してください

    詳細な説明はこちら

    上記に当てはまらず、質問内容が明確になっていない質問には「情報の追加・修正依頼」機能からコメントをしてください。

    質問の評価を下げる機能の利用条件

    この機能を利用するためには、以下の事項を行う必要があります。

回答 2

check解決した方法

0

ひとまず、以下のコードで解決することができました。

解決策をまとめると、1つのレコードを作成するごとにsaveを記述することで解決しました。
イメージ説明

まずは、AccountTransaction(取引)モデルにレコードを作成して保存(save)。
その後、関連付けで子モデルにレコードを作成するbuildメソッドを使い、まずは出金レコードを作成して保存(save)。
ちなみに、1:多の関連付けでレコードを作成する場合は、
@変数.複数系モデル名.build()
とするみたいです。
今回の場合、子モデルであるDepositsAndWithdrawal(入出金)モデルにも作成したいので、
@account_transaction.deposits_and_withdrawals.build
となります。
同じく入金側レコードも、関連付けで子モデルにレコードを作成するbuildメソッドを使って保存(save)。

account_transactions_contoroller.rb

class AccountTransactionsController < ApplicationController

  #取引を作成する 
  def new
    @account_transaction = AccountTransaction.new
  end

  #取引を作成して記録する
  def create
    #以下5行のコードで取引モデル(親モデル)への記録を行う
    @account_transaction = AccountTransaction.new(
      withdrawal_account_id: current_user.basic_income_account.id,
      deposit_account_id: BasicIncomeAccount.find_by(account_number: params[:account_transaction][:deposit_account_id]).id,
      amount: params[:account_transaction][:amount]
      )
    @account_transaction.save#親モデル(取引モデル)へレコードを作成保存する

    #以下6行では取引テーブルと関連付けした入出金テーブル(子モデル)に出金レコードを作成する
    @account_transaction.deposits_and_withdrawals.build(
      transaction_type: "出金",
      basic_income_account_id: current_user.basic_income_account.id,
      amount: -1 * params[:account_transaction][:amount].to_f#マイナス小数点ありの数値化
      )
    @account_transaction.save#子モデル(入出金モデル)に出金側レコードを保存する

    #以下5行では取引テーブルと関連付けした入出金テーブルに入金レコードを作成する
    @account_transaction.deposits_and_withdrawals.build(
      transaction_type: "入金",
      basic_income_account_id: BasicIncomeAccount.find_by(account_number: params[:account_transaction][:deposit_account_id]).id,
      amount: params[:account_transaction][:amount].to_f
      )
#@account_transactionを保存し、保存できたらホーム画面(root_path)へ、失敗なら取引入力画面(new)へ飛ぶ。
    if @account_transaction.save#
    redirect_to root_path
    else
      render "new"
    end
  end

  private
  def account_transaction_params
  params.require(:account_transaction).permit(:deposit_account_id, :amount)
  end
end

一応これで当初達成したかった「親モデルと同時に子モデルに複数のレコードを作成したい」を実行できます。

投稿 2017/12/03 08:43

編集 2017/12/03 08:52

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

0

    #以下5行は出金側のレコードを作成する  
    @deposits_and_withdrawal1 = DepositsAndWithdrawal.new
    @deposits_and_withdrawal1.transaction_type = '出金'
    @deposits_and_withdrawal1.basic_income_account_id = current_user.basic_income_account.id
    @deposits_and_withdrawal1.amount = -1 * params[:account_transaction][:amount]#ここの渡された数値をマイナス数値にしたい
    @account_transaction.deposits_and_withdrawals << @deposits_and_withdrawal1

    ...
    入金側も同様
    ...

    # ここですべて同時に保存される
    @account_transaction.save

投稿 2017/11/30 06:16

  • 回答の評価を上げる

    以下のような回答は評価を上げましょう

    • 正しい回答
    • わかりやすい回答
    • ためになる回答

    評価が高い回答ほどページの上位に表示されます。

  • 回答の評価を下げる

    下記のような回答は推奨されていません。

    • 間違っている回答
    • 質問の回答になっていない投稿
    • スパムや攻撃的な表現を用いた投稿

    評価を下げる際はその理由を明確に伝え、適切な回答に修正してもらいましょう。

  • 2017/11/30 10:58 編集

    Takahito_Ogawaさん、回答ありがとうございます!
    アドバイスを元に、account_transactions_controller.rbの
    コードを修正してみました。(修正したコードが長くコメント欄では見ずらいので、質問文に追記してあります)

    エラーは特に表示されてはいないのですが、
    保存に失敗したようで、テーブルには何も保存されませんでした。

    キャンセル

  • 2017/11/30 21:49

    save実行時のログを見せてもらってもよろしいでしょうか。

    キャンセル

  • 2017/12/01 08:40

    以下が、save時のログになります。
    Started POST "/account_transactions" for 10.0.2.2 at 2017-11-30 23:38:53 +0000
    Cannot render console from 10.0.2.2! Allowed networks: 127.0.0.1, ::1, 127.0.0.0/127.255.255.255
    Processing by AccountTransactionsController#create as HTML
    Parameters: {"utf8"=>"✓", "authenticity_token"=>"BrhH+NkieKuNcWzUVA0nt7ni3L7xNImb54Jvi2Zhn+UbgEEkjWGT974g7Iqn0fiGeERi7Mjhhpx5nUjkeiF52g==", "account_transaction"=>{"deposit_account_id"=>"454839067625", "amount"=>"2525"}, "commit"=>"送金確認"}
    User Load (0.3ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? ORDER BY "users"."id" ASC LIMIT ? [["id", 20], ["LIMIT", 1]]
    BasicIncomeAccount Load (0.2ms) SELECT "basic_income_accounts".* FROM "basic_income_accounts" WHERE "basic_income_accounts"."user_id" = ? LIMIT ? [["user_id", 20], ["LIMIT", 1]]
    BasicIncomeAccount Load (0.1ms) SELECT "basic_income_accounts".* FROM "basic_income_accounts" WHERE "basic_income_accounts"."account_number" = ? LIMIT ? [["account_number", "454839067625"], ["LIMIT", 1]]
    (0.1ms) begin transaction
    BasicIncomeAccount Load (0.2ms) SELECT "basic_income_accounts".* FROM "basic_income_accounts" WHERE "basic_income_accounts"."id" = ? LIMIT ? [["id", 19], ["LIMIT", 1]]
    BasicIncomeAccount Load (0.1ms) SELECT "basic_income_accounts".* FROM "basic_income_accounts" WHERE "basic_income_accounts"."id" = ? LIMIT ? [["id", 18], ["LIMIT", 1]]
    SQL (0.7ms) INSERT INTO "account_transactions" ("amount", "created_at", "updated_at", "withdrawal_account_id", "deposit_account_id") VALUES (?, ?, ?, ?, ?) [["amount", 2525.0], ["created_at", "2017-11-30 23:38:53.745727"], ["updated_at", "2017-11-30 23:38:53.745727"], ["withdrawal_account_id", 19], ["deposit_account_id", 18]]
    (175.6ms) commit transaction
    (0.1ms) begin transaction
    BasicIncomeAccount Load (0.3ms) SELECT "basic_income_accounts".* FROM "basic_income_accounts" WHERE "basic_income_accounts"."id" = ? LIMIT ? [["id", 19], ["LIMIT", 1]]
    SQL (0.6ms) INSERT INTO "deposits_and_withdrawals" ("account_transaction_id", "transaction_type", "amount", "created_at", "updated_at", "basic_income_account_id") VALUES (?, ?, ?, ?, ?, ?) [["account_transaction_id", 24], ["transaction_type", "出金"], ["amount", -2525.0], ["created_at", "2017-11-30 23:38:53.929085"], ["updated_at", "2017-11-30 23:38:53.929085"], ["basic_income_account_id", 19]]
    (7.1ms) commit transaction
    BasicIncomeAccount Load (0.2ms) SELECT "basic_income_accounts".* FROM "basic_income_accounts" WHERE "basic_income_accounts"."account_number" = ? LIMIT ? [["account_number", "454839067625"], ["LIMIT", 1]]
    (0.3ms) begin transaction
    (0.1ms) commit transaction
    (0.1ms) begin transaction
    (0.1ms) rollback transaction
    Rendering account_transactions/new.html.erb within layouts/application
    Rendered account_transactions/new.html.erb within layouts/application (4.4ms)
    Rendered layouts/_shim.html.erb (0.4ms)
    Rendered layouts/_header.html.erb (2.0ms)
    Rendered layouts/_footer.html.erb (0.5ms)
    Completed 200 OK in 265ms (Views: 52.3ms | ActiveRecord: 186.0ms)

    キャンセル

  • 2017/12/01 11:03

    ありがとうございます。ログを注意深く確認しましたが、どうもsaveが3回実行されているようです。意図した動作と異なるので、前のコードのまま実行して、そのログを表示してしまっているのではないかと疑っているのですが、ご確認いただけませんでしょうか。

    キャンセル

  • 2017/12/01 21:12 編集

    申し訳ありません。ご指摘の通り、余分なsaveを残したまま実行をしていました。
    以下は余分なsaveを削ったログになります。

    Started POST "/account_transactions" for 10.0.2.2 at 2017-12-01 10:29:37 +0000
    Cannot render console from 10.0.2.2! Allowed networks: 127.0.0.1, ::1, 127.0.0.0/127.255.255.255
    Processing by AccountTransactionsController#create as HTML
    Parameters: {"utf8"=>"✓", "authenticity_token"=>"chnC9+Td0RGG5Cox95mXbWvhP8khfGt4xMMtgPejmnxJ5WgqkH2lyvhrhgMNtYas6XzG7F10GKCkfmLxjLdJTA==", "account_transaction"=>{"deposit_account_id"=>"454839067625", "amount"=>"2828"}, "commit"=>"送金確認"}
    User Load (0.4ms) SELECT "users".* FROM "users" WHERE "users"."id" = ? ORDER BY "users"."id" ASC LIMIT ? [["id", 20], ["LIMIT", 1]]
    BasicIncomeAccount Load (0.2ms) SELECT "basic_income_accounts".* FROM "basic_income_accounts" WHERE "basic_income_accounts"."user_id" = ? LIMIT ? [["user_id", 20], ["LIMIT", 1]]
    BasicIncomeAccount Load (0.2ms) SELECT "basic_income_accounts".* FROM "basic_income_accounts" WHERE "basic_income_accounts"."account_number" = ? LIMIT ? [["account_number", "454839067625"], ["LIMIT", 1]]
    CACHE BasicIncomeAccount Load (0.0ms) SELECT "basic_income_accounts".* FROM "basic_income_accounts" WHERE "basic_income_accounts"."account_number" = ? LIMIT ? [["account_number", "454839067625"], ["LIMIT", 1]]
    (0.2ms) begin transaction
    BasicIncomeAccount Load (0.1ms) SELECT "basic_income_accounts".* FROM "basic_income_accounts" WHERE "basic_income_accounts"."id" = ? LIMIT ? [["id", 19], ["LIMIT", 1]]
    BasicIncomeAccount Load (0.2ms) SELECT "basic_income_accounts".* FROM "basic_income_accounts" WHERE "basic_income_accounts"."id" = ? LIMIT ? [["id", 18], ["LIMIT", 1]]
    CACHE BasicIncomeAccount Load (0.0ms) SELECT "basic_income_accounts".* FROM "basic_income_accounts" WHERE "basic_income_accounts"."id" = ? LIMIT ? [["id", 19], ["LIMIT", 1]]
    (0.1ms) rollback transaction
    Rendering account_transactions/new.html.erb within layouts/application
    Rendered account_transactions/new.html.erb within layouts/application (3.1ms)
    Rendered layouts/_shim.html.erb (0.3ms)
    Rendered layouts/_header.html.erb (1.0ms)
    Rendered layouts/_footer.html.erb (0.4ms)
    Completed 200 OK in 58ms (Views: 39.2ms | ActiveRecord: 1.4ms)

    キャンセル

  • 2017/12/01 21:54

    ありがとうございます。save失敗後のnew.html.erbに、@account_transaction.errors.to_yamlをdiv要素に入れて表示してもえますか?
    エラーメッセージが出るはずです。

    キャンセル

  • 2017/12/01 22:18 編集

    度々ありがとうございます。
    以下の内容がsave失敗後の入力フォーム画面に表示されたエラーになります。
    ・Deposits and withdrawals is invalid

    --- !ruby/object:ActiveModel::Errors base: !ruby/object:AccountTransaction concise_attributes: - !ruby/object:ActiveRecord::Attribute::FromDatabase name: id - !ruby/object:ActiveRecord::Attribute::FromUser name: amount value_before_type_cast: '256' original_attribute: !ruby/object:ActiveRecord::Attribute::FromDatabase name: amount type: !ruby/object:ActiveModel::Type::Decimal precision: scale: limit: - !ruby/object:ActiveRecord::Attribute::FromDatabase name: created_at - !ruby/object:ActiveRecord::Attribute::FromDatabase name: updated_at - !ruby/object:ActiveRecord::Attribute::FromUser name: withdrawal_account_id value_before_type_cast: 19 original_attribute: !ruby/object:ActiveRecord::Attribute::FromDatabase name: withdrawal_account_id type: &1 !ruby/object:ActiveModel::Type::Integer precision: scale: limit: range: !ruby/range begin: -2147483648 end: 2147483648 excl: true - !ruby/object:ActiveRecord::Attribute::FromUser name: deposit_account_id value_before_type_cast: 18 original_attribute: !ruby/object:ActiveRecord::Attribute::FromDatabase name: deposit_account_id type: *1 new_record: true active_record_yaml_version: 2 messages: :deposits_and_withdrawals: - is invalid :送金相手の口座番号: [] :deposit_account_id: [] :送金額: [] :amount: [] details: :deposits_and_withdrawals: - :error: :invalid - :error: :invalid

    キャンセル

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

ただいまの回答率

91.36%

関連した質問

同じタグがついた質問を見る

  • Ruby

    5168questions

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

  • Ruby on Rails 5

    77questions