LoginSignup
1
1

More than 5 years have passed since last update.

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

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の順序を知った上で、どう実際のコーディングに落とし込むかの良い練習になりました。

1
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
1