views:

72

answers:

2

I have an order model that has_many :items. Each item has item.price for the cost of said item. I want to add up all of the item prices in the order for a order.total_price. Right now I'm doing that with

after_save :update_total_price, :if => "self.saved.nil? "


def update_total_price
     self.total_price = Item.find(item_ids).inject(0){|sum,item| sum + (item.price * item.amount) } #amount is how many items there are
     self.saved = 1
     self.save if self.saved
end

This works just fine the first time that I put in the info, but if I try to edit the order, the total_price doesn't get updated because update_total_price doesn't get called because self.saved is not nil.

What can I do to make it so that updating the model will update it, but won't keep on doing an infinite loop of calling .save?

+3  A: 

Why not have the update_total_price NOT save the data again.

just set the value in before_update:

before_save :update_total_price

def update_total_price
  self.total_price = items.find(:all).inject(0){|sum,item| sum + (item.price * item.amount) } 
end
Toby Hede
This works wonderfully. Thanks. I had totally forgotten about before_save
Reti
Actually, this doesn't work because `Item.find(item_ids)` expects the items' ids, but they haven't been created yet, so this causes an error. How could I make it work?
Reti
It only works on updating, not on making new records.
Reti
Just add a check on item_ids.
Toby Hede
I don't know what you mean :\
Reti
when are the id's created? on the initial save?
Toby Hede
I believe so. But when the Item.find(item_ids) is called, it errors and says you can't find without an ID. I'm guessing that it trys to get the Item before it creates them.
Reti
If it helps, item is a nested object inside order with a has_many association.
Reti
rather than Item.find you can just use entries.find(:all)
Toby Hede
sorry, items.find or items.all - working on something else and have entries on the brain.
Toby Hede
It seems you don't have the `have_many :items` relation defined in your order model. I guess you need to add that, and use the `items` instead of the `Item.find(item_ids)`. Mind you: that is a weird id-name, i would expect the `Item` to have an `order_id` column.
nathanvda
@nathanvda I do have the correct association setup and items does have an `order_id` column.@toby-hede items.all works the second time around I submit the form. It doesn't calculate the total price when I create a new order, only when I update it. It also doesn't work if I try and update it more than once.
Reti
A: 
after_save :update_total_price

def update_total_price          
   self.total_price = find_total_price
   self.save_without_callbacks
end

def find_total_price
  Item.find(item_ids).inject(0){|sum,item| sum + (item.price * item.amount) 
end
mark
This will cause another infinite loop. The save(false) avoids validations, but not callbacks.
Andrew Kuklewicz
Oops. How about the above edit? Actually Toby's answer is better but I'll leave this here anyway.
mark