I recently added counter cache to one of my assocations to try and solve a validation problem. ie. If an object has too many associated objects already then it would be invalid.
Easy, set up counter cache and update your existing database with the current totals, however this doesn’t work anymore. It doesn’t set them at all.
I can see all the updates in my log, but minus the ‘requirements_count’ which was the whole point :S
1Requirement Load (0.9ms) SELECT * FROM "requirements" WHERE ("requirements".subscription_id = 158) ORDER BY created_on asc 2 Subscription Update (0.9ms) UPDATE "subscriptions" SET "updated_on" = '2009-07-11 17:44:35.463992' WHERE "id" = 158 3 Requirement Load (1.1ms) SELECT * FROM "requirements" WHERE ("requirements".subscription_id = 264) ORDER BY created_on asc 4 Subscription Update (0.9ms) UPDATE "subscriptions" SET "updated_on" = '2009-07-11 17:44:35.470008' WHERE "id" = 264
I found this in the ActiveRecord changelog :( It states this is now a readonly field. Under normal circumstances I agree it should be readonly but what about when you just want to get your data into the right state?
1*2.0.0 [Preview Release]* (September 29th, 2007) [Includes duplicates of changes from 1.14.2 - 1.15.3] 2 3* Add attr_readonly to specify columns that are skipped during a normal ActiveRecord #save operation. Closes #6896 [Dan Manges] 4 5 class Comment < ActiveRecord::Base 6 # Automatically sets Article#comments_count as readonly. 7 belongs_to :article, :counter_cache => :comments_count 8 end 9 10 class Article < ActiveRecord::Base 11 attr_readonly :approved_comments_count 12 end
The solution it seems is to redeclare the class you are trying update the count in in the migration class. I cut and pasted this from a mailing list post I found. This seems stupidly awkward a solution and took far too long to diagnose this problem.
1class AddRequirementsCountToSubscriptions < ActiveRecord::Migration 2 3 class Subscription < ActiveRecord::Base 4 5 has_many :requirements 6 7 def reset_column_information 8 generated_methods.each {|name| undef_method(name) } 9 @column_names = @columns = @columns_hash = @content_columns = @dynamic_methods_hash = @generated_methods = @inheritence_column = nil 10 end 11 12 end 13 14 def self.up 15 add_column :subscriptions, :requirements_count, :integer, :default => 0 16 17 Subscription.reset_column_information 18 19 Subscription.all.each do |s| 20 puts "UPDATING #{s.id}" 21 puts "#{s.requirements.length}" 22 s.update_attribute :requirements_count, s.requirements.length 23 end 24 end 25 26 def self.down 27 remove_column :subscriptions, :requirements_count 28 end 29 30end
sigh