ruby-on-railsrails-activestoragecounter-cache

Rails Counter Cache with Active Storage


I have a Account model with 3 attachments, using Active Storage, has_many_attached :attachments.

I want to know how many attached files the account has, the most efficient way (aka no joins)

The only solution I found is Account.last.attachments.count or .size, but it makes two query: one for the account and one using active_storage_attachments table.

Is there a way to counter cache the number of attachments?

Thank you in advance

EDIT

Of course I can set up my own database field and count it, I want to know if there is some default

EDIT

I tried to do has_many_attached :attachments, counter_cache: true, but it gives an error


Solution

  • I had similar issue with a model that had 7 file attachments, which I needed to count using counter cache instead of the a database query. The trick is that you have to specify counter cache reference in the child model, which has the belongs_to :ParentModel - ActiveStorage::Attachment in my case. ActiveStorage::Attachment is a 'behind-the-scenes' model of Rails so I monkey patched it. But instead of implementing counter cache by adding counter_cache: :true I decided to implement it through callbacks. I created a module for the ParentModel with following structure:

    module ParentModel::ModuleName
      extend ActiveSupport::Concern
    
      class ActiveStorage::Attachment
        require 'active_record/counter_cache'
    
        before_create :after_create_action, if: :record_parent_model?
        before_destroy :after_destroy_action, if: :record_parent_model?
    
        def after_create_action
          ParentModel.increment_counter(:attachments_count, record_id, touch: true)
        end
    
        def after_destroy_action
          ParentModel.decrement_counter(:attachments_count, record_id, touch: true)
        end
    
        def record_parent_model?
          record_type == 'ParentModel'
        end
      end
    end
    

    And I created a migration to add :attachments_count column to the ParentModel as in case of a straight forward counter cache implementation.