The following blog post contains material either currently found or soon to be incorporated into my new book, "Easy Active Record for Rails Developers". Among many other topics, you'll learn about model generation, migrations, validations, associations, scopes, joins, includes, forms integration, nested forms, and model testing with RSpec and FactoryGirl. The book is now available, head over to this website's home page to learn more.

It is often useful to know how many records are associated with a model identified as being the parent in a belongs_to relationship. This information could be used when presenting a list of the U.S. states in a list group, akin to how works by presenting each category name and the number of associated locations. The easiest way to retrieve this count is by formalizing the other end of the belongs_to relationship by indicating that a state has_many locations:

class State < ActiveRecord::Base

  has_many :locations                                                       


With this in place, you can easily determine how many locations are associated with a particular state:

>> state = State.find_by_abbreviation("OH")                                 
>> state.locations.size                                                     
   (0.3ms)  SELECT COUNT(*) FROM `locations`  WHERE `locations`.`state_id` = 36
=> 84                                                                       

However, as you can see by the above example, determining this number requires execution of a COUNT(*) query, something we’d like to avoid if possible. Using the counter_cache option, you can! When enabled, this option will update a field in the parent model’s table every time the number of associated records changes, and then whenever size is called, Active Record will instead retrieve the value found in that field rather than perform the costly COUNT(*) query.

Enabling this option is actually a two step process. First you’ll want to add the counter_cache option to the belongs_to declaration:

class Location < ActiveRecord::Base                                                                        

  belongs_to :state, counter_cache: true                                    


Then, you’ll want to create a migration affecting the parent model’s table, adding an integer-based column named locations_count:

$ rails g migration add_locations_count_to_states locations_count:integer   

This will generate the following migration:

class AddLocationsCountToStates < ActiveRecord::Migration                   
  def change                                                                
    add_column :states, :locations_count, :integer                          

Note that the field name (locations_count) is adapted to the name of the child model’s table. You’ll need to modify the name accordingly for your own application’s purposes.

Run this migration and Active Record will begin updating the locations_count field every time the count changes due to the addition , modification, or deletion of a relevant association. But it does not take into account the number of associations that might already be in place. Therefore if you add this feature after data has already been loaded into the application, you’ll need to take additional steps to ensure the count is correct. This is easily accomplished by adding code similar to the following to seeds.rb and executing the rake db:seed task within the appropriate environment:

states = State.all                                                          
states.each do |state|                                                      
  state.locations_count = state.locations.count                                                                                      

Like what you read? There’s plenty more where this came from in my new book, “Easy Active Record for Rails Developers”!