Recently while working on one of our project there was an requirement of tracking count of records associated with has-many association. But also there was "is_hidden" flag set on some of the records which I do not suppose to count in counter as those were hidden records. So default counter_cache
option of rails active-record was not appropriate in this case which automatically counts and caches the number of associated records and keeps cache updated. Refer following model code for this.
class Order < ActiveRecord::Base
belongs_to :customer, counter_cache: true
end
class Customer < ActiveRecord::Base
has_many :orders
end
In above case @customer.orders.size
will return the current count of customer orders without hitting database but for this you also need to have orders_count
column in Customer model which will be used by active-record
to maintain counter value.
But as I didn't want to count hidden records, I added custom code in after_save
and after_destroy
callbacks of Topic model to increment and decrement associated topics count for category if record is having is_hidden
flag as FALSE. I used column name as topics_count
in Category model to store/increment/decrement this count.
My models were as follows:
class Topic < ActiveRecord::Base
belongs_to :category
end
class Category < ActiveRecord::Base
has_many :topics
end
And my custom code to increment and decrement topics_count
was:
class Topic < ActiveRecord::Base
after_save :update_topics_count_for_category
after_destroy :decrement_topics_count_for_category
def decrement_topics_count_for_category
category.decrement!(:topics_count) unless is_hidden
end
def update_topics_count_for_category
if (id_changed? ? !is_hidden : is_hidden_changed?)
is_hidden ? category.decrement!(:topics_count) : category.increment!(:topics_count)
end
end
end
This solution was maintaining topics count as expected but then in one scenario I created topic using has many association method
i.e. @category.topics.create(topic_params)
This was leading to double increment in value of topics_count
and I was confused due to this random issue as creating topic
as Topic.create(..., category_id: category.id)
was not leading to this issue. Obviously first doubt was on counter_cache
feature of active-record. So while debugging it that direction I found that in Rails 4.2.x, though counter_cache option is not set to true on belongs_to association, using has-many association method for creating associated record, automatically increments the column value if column such as "#{table_name}_count" (in this case topics_count) is found in belonging model (in this case "Category").
And as I was incrementing topics_count
value in after_save
callback also, it was getting incremented twice.
Finally renaming topics_count
to topic_counter
fixed this issue.
For more information you can refer this issue details here: automatic counter_cache issue in Rails 4.2.x