counter_cache with conditions
Asked Answered
S

2

9

I know I can use callbacks but this should be feasible. I did lengthly searching and no result. This is what I thought would have worked.

def User < ActiveRecord::Base
  has_many :documents
  has_many :draft_docs     , :class_name => 'Document', :conditions => { :status => 'draft' }
  has_many :published_docs , :class_name => 'Document', :conditions => { :status => 'published' }
  has_many :private_docs   , :class_name => 'Document', :conditions => { :status => 'private' }
end

def Document < ActiveRecord::Base
  belongs_to :user      , :counter_cache => true
  belongs_to :user      , :inverse_of => :draft_docs    , :counter_cache => true
  belongs_to :user      , :inverse_of => :published_docs, :counter_cache => true
  belongs_to :user      , :inverse_of => :private_docs  , :counter_cache => true
end

Not working as planned, as you can see its updating documents_count instead of published_docs_count.

ruby-1.9.2-p180 :021 > User.reset_counters 2, :published_docs  User Load (0.4ms)  SELECT `users`.* FROM `users` WHERE `users`.`id` = 2 LIMIT 1
   (0.7ms)  SELECT COUNT(*) FROM `documents` WHERE `documents`.`user_id` = 2 AND `documents`.`status` = 'published'
   (2.2ms)  UPDATE `users` SET `documents_count` = 233 WHERE `users`.`id` = 2
 => true
Smoke answered 18/9, 2011 at 3:18 Comment(0)
D
1

Use the counter_culture gem.

  • Add three columns to the users table.

    add_column :users, :draft_documents_count, :integer, null: false, default: 0
    add_column :users, :published_documents_count, :integer, null: false, default: 0
    add_column :users, :private_documents_count, :integer, null: false, default: 0
    
  • Decorate the Document model

    class Document
      counter_culture :user, :column_name => Proc.new do |doc|
        if %w(draft published private).include?(doc.status) 
          "{doc.status}_documents_count"
        end
      end    
    end
    
  • Run a command in the console to seed the counts for current rows

    Document.counter_culture_fix_counts
    

Old answer

You can supply the counter column name (apart from true) to the counter_cache option.

class Document < ActiveRecord::Base
  belongs_to :user, :counter_cache => true
  belongs_to :user, :inverse_of => :draft_docs, 
                                   :counter_cache => :draft_docs_count
  belongs_to :user, :inverse_of => :published_docs, 
                                   :counter_cache => :published_docs_count
  belongs_to :user, :inverse_of => :private_docs, 
                                   :counter_cache => :private_docs_count
end
Department answered 18/9, 2011 at 4:30 Comment(2)
It doesn't work, It still only tries to update documents_count.Smoke
This does not work. At least not in Rails 4. I suspect it does not work in Rails 3.x either.Embarrass
S
2

This is because you use the same name for all the associations.

belongs_to :user, :counter_cache => true
belongs_to :user_doc, :class_name => "User", 
           :inverse_of => :draft_docs, :counter_cache => :draft_docs_count

This functionality is implemented by adding a callback to increment the count, and in your case, the target is document.user since you used the same name for everything.

Stillborn answered 22/7, 2012 at 1:32 Comment(2)
I don't see how this answer is different from the one provided by Harish Shetty. On SO it is recommended to bring something new to a question when replying. :)Deaconess
This does not work in Rails 4. I suspect it will not work in Rails 3.x either. Have you tested this?Embarrass
D
1

Use the counter_culture gem.

  • Add three columns to the users table.

    add_column :users, :draft_documents_count, :integer, null: false, default: 0
    add_column :users, :published_documents_count, :integer, null: false, default: 0
    add_column :users, :private_documents_count, :integer, null: false, default: 0
    
  • Decorate the Document model

    class Document
      counter_culture :user, :column_name => Proc.new do |doc|
        if %w(draft published private).include?(doc.status) 
          "{doc.status}_documents_count"
        end
      end    
    end
    
  • Run a command in the console to seed the counts for current rows

    Document.counter_culture_fix_counts
    

Old answer

You can supply the counter column name (apart from true) to the counter_cache option.

class Document < ActiveRecord::Base
  belongs_to :user, :counter_cache => true
  belongs_to :user, :inverse_of => :draft_docs, 
                                   :counter_cache => :draft_docs_count
  belongs_to :user, :inverse_of => :published_docs, 
                                   :counter_cache => :published_docs_count
  belongs_to :user, :inverse_of => :private_docs, 
                                   :counter_cache => :private_docs_count
end
Department answered 18/9, 2011 at 4:30 Comment(2)
It doesn't work, It still only tries to update documents_count.Smoke
This does not work. At least not in Rails 4. I suspect it does not work in Rails 3.x either.Embarrass

© 2022 - 2024 — McMap. All rights reserved.