を使用して発生した問題に対する質問/回答を見つけるのに少し時間がかかりましたaccepts_nested_attributes_for
。しかし、答えは、不可能ではないにしても、それは難しいと言っただけです!。accepts_nested_attributes_for
やや複雑なアプローチですが、子モデルの計算に基づいてモデルを検証しようとしている場合を除いて、機能します。私は計算の問題を克服する方法を見つけたかもしれません。
私は、次の基本モデルを備えたWebベースの複式簿記アプリケーションに取り組んでいます。
class Account < ApplicationRecord
has_many :splits
has_many :entries, through: :splits
end
class Entry < ApplicationRecord
has_many :splits, -> {order(:account_id)}, dependent: :destroy, inverse_of: :entry
validate :balanced?
end
class Split < ApplicationRecord
belongs_to :entry, inverse_of: :splits
belongs_to :account
validates_associated :account
validates_associated :entry
end
エントリ(トランザクション)には、少なくとも2つのスプリットが必要です。スプリットのAmount属性(または借方/クレジット)の合計は0に等しくなければなりません。私validate :balanced?
はそれを処理しますが、明らかなJavascriptエラーにより不均衡なエントリが許可されました。まだバグを追跡していませんが、エントリのバランスが取れてvalid?
いなかったため、追加しようとした新しいスプリットでは機能しない(falseを返す)ため、更新できませんでした。
元帳accepts_nested_attributes_for
フォームは、不均衡なトランザクションの送信を許可しないはずのJavascriptを少し終了しました。バランスが取れていますか?作成時にエラーを設定しませんでしたが、更新時にエラーが発生しました。それを修正するための私のアプローチは、機能しない検証を使用していませんが、@entry.update(entry_params)
:と組み合わせて呼び出されるメソッドに依存しています。
class Entry < ApplicationRecord
has_many :splits, -> {order(:account_id)}, dependent: :destroy, inverse_of: :entry
# validate :balanced? # took this out since its after the fact, balanced? method can still be called
accepts_nested_attributes_for :splits,
:reject_if => proc { |att| att[:amount].to_i.zero? && att['account_id'].to_i.zero?},
allow_destroy: true
def valid_params?(params)
split_sum = 0
params_hash = params.to_h
params_hash[:splits_attributes].each{|k,s| split_sum += s[:amount].to_i if s[:_destroy].to_i.zero?}
unless split_sum.zero?
errors.add(:amount, "Unbalanced: debits, credits must balance")
return false
else
return true
end
end
end
end
# update action from Entry Controller
def update
respond_to do |format|
if @entry.valid_params?(entry_params) && @entry.update(entry_params)
format.html { redirect_to account_path(session[:current_acct]), notice: 'Entry was successfully updated.' }
format.json { render :show, status: :ok, location: @entry }
else
# ... errr
end
end
end
繰り返しになりますが、これは、パラメータの検証と、この条件では機能しないモデルの検証にすぎません。
これは回答2とほぼ同じかもしれませんが、コールバックを使用せず、コントローラーを呼び出すだけです。