简体   繁体   中英

Multiple counter_cache in Rails model

I'm learning Rails, and got into a little problem. I'm writing dead simple app with lists of tasks, so models look something like that:

class List < ActiveRecord::Base
  has_many :tasks
  has_many :undone_tasks, :class_name => 'Task',
                          :foreign_key => 'task_id',
                          :conditions => 'done = false'
  # ... some validations
end

Table for List model has columns tasks_counter and undone_tasks_counter .

class Task < ActiveRecord::Base
  belongs_to :list, :counter_cache => true
  # .. some validations
end

With such code there is attr_readonly :tasks_counter for List instances but I would like to have a counter for undone tasks as well. Is there any way of having multiple counter cached automagically by Rails.

So far, I've managed to create TasksObserver that increments or decrements Task#undone_tasks_counter , but maybe there is a simpler way.

Have you tried it with a custom-counter-cache column? The doc here: http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html

It suggests that you can pass a column-name to the counter_cache option, which you may well be able to call twice eg

belongs_to :list, :counter_cache => true     # will setup tasks_count
belongs_to :list, :counter_cache => :undone_tasks_count

Note: not actually tested.

ez way.

1) first counter - will do automatically

2) Manually "correct"

  AnotherModelHere

    belongs_to :user, counter_cache: :first_friends_count


    after_create  :provide_correct_create_counter_2
    after_destroy :provide_correct_destroy_counter_2

    def provide_correct_create_counter_2
      User.increment_counter(:second_friends_count, another_user.id)
    end

    def provide_correct_destroy_counter_2
      User.decrement_counter(:second_friends_count, another_user.id)
    end

Most probably you will need counter_culture gem, as it can handle counters with custom conditions and will update counter value not only on create and destroy, but for updates too:

class CreateContainers < ActiveRecord::Migration[5.0]
  create_table :containers, comment: 'Our awesome containers' do |t|
    t.integer  :items_count,        default: 0, null: false, comment: 'Caching counter for total items'
    t.integer  :loaded_items_count, default: 0, null: false, comment: 'Caching counter for loaded items'
  end
end

class Container < ApplicationRecord
  has_many   :items, inverse_of: :container
  has_many   :loaded_items, -> { where.not(loaded_at: nil) }, 
             class_name: 'Item',
             counter_cache: :loaded_items_count
             # Notice that you can specify custom counter cache column name
             # in has_many definition and AR will use it!
end

class Item < ApplicationRecord
  belongs_to :container, inverse_of: :items, counter_cache: true
  counter_culture :container, column_name: proc { |model| model.loaded_at.present? ? 'loaded_items_count' : nil }
  # But this column value will be handled by counter_culture gem
end

I'm not aware of any "automagical" method for this. Observers seems good for this, but I personally prefer using callbacks in model ( before_save , after_save ).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM