Eyüp Atiş

Rails has_many through association with counter cache argument

Counter cache argument prevents you from making unnecessary `SELECT COUNT (*) requests.

We want to make unnecessary @course.users call for each course.

Next association is a basic example for :has_many, :through association.

has_many :subscriptions
has_many :users, through: :subsriptions
has_many :subscriptions
has_many :courses, through: :subsriptions
belongs_to :user
belongs_to :course

We must add a counter cache for course, as users_count

belongs_to :user
belongs_to :course, counter_cache: :users_count

After that we must add a field to Course model for storing users_count

rails g migration add_users_count_to_courses users_count:integer

Open the created migration file and set default value to 0 and prevent from nil records

class AddUsersCountToCourses < ActiveRecord::Migration
  def change
    add_column :courses, :users_count, :integer, default: 0, null: false
  end
end

After this step, @course.users will return 0, because we set the defaut value to it.

For fix this situation we must generate another migration file and fill @course.users_count field with correct values

rails g migration cache_course_users_count

Open the file and fill :users_count field

class CacheCourseUsersCount < ActiveRecord::Migration
  def up
    Course.find_each do |course|
      users_count = Subscription.where(course_id: course.id).size
      course.update_attributes(users_count: users_count)
    end
  end

  def down
  end
end

That’s all. Now you are storing users_count in database and cache it, so performance of your app increased.

Good for you!

This project is maintained by eyupatis