So today I learned that if you have:
class Invoice < ActiveRecord::Base
has_many :transactions
before_save :cache_transaction_total
def cache_transaction_total
self.transaction_total = transactions.sum(:total) || 0
end
end
class Transaction < ActiveRecord::Base
belongs_to :invoice
end
And you open a rails console and do something innocuous like:
$ i = Invoice.create
The query that aggregates the transactions in the before_save callback is something like:
SELECT SUM("transactions"."total") AS sum_id
FROM "transactions" WHERE "transactions"."invoice_id" IS NULL
Which rather than aggregating all the (non-existant) transactions associated to the new invoice, aggregates all the transactions which aren't associated to an invoice at all - which gives a very different result. In my case, most transactions are not listed to an invoice so instead of caching 0
it caches something more like 1,000,000
.
I guess the solution is to run the update in an after_save callback instead -- but does this behaviour not seem completely different to what we would expect?
Thoughts?
The invoice won't have an id or be in the db at all until after it's saved... hence you're going to have to be a bit cleverer about how you do it, or stick it in after_save (with the fun that you're going to need to save in after_save and make sure you don't go all inception-mode on the code).
Sam