Rails counter cache associations
Rails provides an ability to cache count of associated table records. This gives an edge that avoids querying to get number of records of associated table.
This is useful for having count of has many association records. It is available on belongs_to options on model.
Let’s take an example.
We have a users
table.
User
model given below.
class Users < ApplicationRecord
has_many :posts
end
And a user
has many posts
.
Post
model given below
class Post < ApplicationRecord
belongs_to :user
end
Without counter_cache in Rails
Let’s say we want to list users with a count of posts by a user. First, we will fetch posts to be displayed in controller action.
class PostsController < ApplicationController
def index
# let's display details of last 10 users
@users = User.last(10)
end
end
Then, we will display them in view erb
as given below.
<!-- app/views/users/index.html.erb -->
<h1>Users</h1>
<table>
<thead>
<th>
<td>ID</td>
<td>Name</td>
<td>Email</td>
<td>Number of Posts</td>
</th>
</thead>
<tbody>
<% @users.each do |user| %>
<td><% user.id %></td>
<td><% user.name %></td>
<td><% user.email %></td>
<td><% user.posts.size %></td>
<% end %>
</tbody>
</table>
Now, this works well to render count of posts per user.
<td><% user.posts.size %></td>
But, this will query posts
for every user object being looped over.
With counter_cache
1. Add a new column
Add a field on users to keep the track of number of posts by the user. We will need to add the field with following naming convention.
<association_name>_count
In the case discussed above, we will use column name given below.
posts_count
2. Mention counter_cache on association
We will need to add counter_cache
option on post belongs_to user association.
class Post < ApplicationRecord
belongs_to :user, counter_cache: true
end
That’s all.
This will make sure that posts_count
on users
has updated value
of
number of posts for the user.
3. Access cached counter value
We don’t need to change the way we access number of records of associated table.
<% user.posts.size %>
This will work and use value from newly added column.
4. Populate count value of existing records
To populate values for the newly added column, Rails provides a method reset_counters.
We can populate counter with a migration with looping over all users
as given below.
User.find_each do |user|
user.reset_counters(user.id, :posts)
end
Reference
Subscribe to Ruby in Rails
Get the latest posts delivered right to your inbox
