Posted at

親モデルのメソッドよりも子モデルのメソッドを先に動作させる


初期状態

子モデルに書いてあるメソッドの結果が、親モデルのプロパティに代入されるという場面がありました。

例えば。。。。

User 1----* Book

の状況で、bookの中にpriceというカラムがあり、userが所有しているbookのprice合計を、userの中のcostというカラムに代入するという仕様があったとします。また、costに入れる前にbookモデル内で消費税を自動で計算してbookのpriceに入れます。その合計をcostに入れます

スクリーンショット 2018-11-17 22.10.34.gif

(not_include_tax_priceは消費税抜きの額)

そして、そのcostのカラムが計算された後に、userのインスタンスが保存されなければならないとします。

カラムには正しい値を入れるため、before_validationをかけます。


問題


user.rb

before_validation :sum_price

def sum_price
self.cost = self.books.map{|b| b.price}.sum
end



book.rb

before_validation :sales_tax

def sales_tax
self.price = not_include_tax_price * 1.08
end


spec書いてましたので、そこでやりますが、、、


spec.rb

user = User.first

user.books.new(price: 100)
user.save

上のやり方でかくと、userのcostは消費税が勘案されていない状態で計算されます。

なぜかというと、

Railsのcallbackについて調べた

こちらをみていただくと、親のbefore_validaitonが働いてから、子のbefore_validaitonが働くようになっています。

ということは、def sale_taxよりもsum_priceが先に計算されることとなり、消費税が働かなくなる。。。


解決策

もうほぼ上のURLに書いてありますが、親before_saveは子before_validationの後に来ます。

ということは


user.rb

before_save :sum_price

def sum_price
self.cost = self.books.map{|b| b.price}.sum
end



book.rb

before_validation :sales_tax

def sales_tax
self.price = not_include_tax_price * 1.08
end


としてあげれば(sum_priceをbefore_saveに変更)、消費税がしっかり計算されてcostに入ります。


まとめ

callbackの順序を知った上で、どう実際のコーディングに落とし込むかの良い練習になりました。