I’m sure you read Frederick’s response about the counter_cache being readonly.
The way to properly populate these during a migration is to redeclare the Learner class as an inner-class of the Migration itself, specifying only minimal functionality.
class AddCounterCacheToLearners < ActiveRecord::Migration
class Learner < ActiveRecord::Base
def reset_column_information
# re-implement reset_column_information functionality here
end
end
def self.up
add_column :learners, :learner_sessions_count, :integer, :default => 0
Learner.reset_column_information
end
def self.down
remove_column :learners, :learner_sessions_count
end
end
Thanks Frederic and Duncan,
FYI: here's the complete migration for the simple test case that adds a counter_cache AND sets the value.
class AddCounterCacheToLearners < ActiveRecord::Migration
class Learner < ActiveRecord::Base
has_many :learner_sessions
def reset_column_information
# re-implement reset_column_information functionality here
generated_methods.each { |name| undef_method(name) }
@column_names = @columns = @columns_hash = @content_columns = @dynamic_methods_hash = @generated_methods = @inheritance_column = nil
end
end
def self.up
add_column :learners, :learner_sessions_count, :integer, :default => 0
Learner.reset_column_information
Learner.find(:all).each do |learner|
count = learner.learner_sessions.count
learner.update_attribute(:learner_sessions_count, count)
end
end
def self.down
remove_column :learners, :learner_sessions_count
end
end
Thanks for that pointer Damian. I didnm't know about the class method Model.update_counters.
I've simplified my migration as follows:
class AddCounterCacheToLearners < ActiveRecord::Migration
def self.up
add_column :learners, :learner_sessions_count, :integer, :default => 0
Learner.reset_column_information
Learner.find(:all).each do |learner|
change_in_count = learner.learner_sessions.count - learner.learner_sessions_count
Learner.update_counters(learner.id, :learner_sessions_count => change_in_count)
end
end
def self.down
remove_column :learners, :learner_sessions_count
end
end
While this is better than my previous solution it still seems a bit ugly.
I like your proposed patch but it's marked as "wontfix".
In my simplified migration above this section:
Learner.reset_column_information
Learner.find(:all).each do |learner|
change_in_count = learner.learner_sessions.count - learner.learner_sessions_count
Learner.update_counters(learner.id, :learner_sessions_count => change_in_count)
end
is equivalent to the suggestions at the end of your patch discussion for a class method something like this:
Learner.update_counter_cache
I like some of the ideas here, and I've been annoyed by this issue
quite a few times. Did a new ticket get created, or was a consensus
about an approach reached?
- Trevor
No consensus :-/
I think the resolution was "we don't want yet another migration helper" (?).
Pratik1
(Pratik)
October 14, 2008, 8:21pm
7
I like the idea of adding a class method like
Model.reset_counter_cache(:column_name)
I don't think there is a new ticket.
I like that as well. I made a new ticket:
http://rails.lighthouseapp.com/projects/8994-ruby-on-rails/tickets/1211-reset_counter_cache
This is probably something I can work on, unless somebody else is
chomping at the bit.