Using named_scope with counts of child models

后端 未结 2 1281
刺人心
刺人心 2020-12-16 07:12

I have a simple parent object having many children. I\'m trying to figure out how to use a named scope for bringing back just parents with specific numbers of children.

相关标签:
2条回答
  • 2020-12-16 07:51

    I would use the counter cache for this. Therefore you need the following migration:

    class AddBarCount < ActiveRecord::Migration
      def self.up  
        add_column :foos, :bars_count, :integer, :default => 0  
    
        Foo.reset_column_information  
        Foo.all.each do |p|  
          p.update_attribute :bars_count, p.bars.length  
        end  
      end  
    
      def self.down  
        remove_column :foos, :bars_count  
      end
    end
    

    Than you need too change you Bar model like this:

    class Bar < ActiveRecord::Base
      belongs_to :foo, :counter_cache => true
    end
    

    Now the count of bars is cached in the foo model, that will speed up your queries for the count of bars.

    Your named_scopes then have too look like this:

    #rails 2
    named_scope :with_no_bars, :conditions => { :bars_count => 0 }
    named_scope :with_one_bar, :conditions => { :bars_count => 1 }
    named_scope :with_more_than_one_bar, :conditions => ["bars_count > 1"]
    
    #rails 3 & ruby 1.9+
    scope :with_no_bars, where(bars_count: 0)
    scope :with_one_bar, where(bars_count: 1)
    scope :with_more_than_on_bar, where("bars_count > 1")
    
    #rails 4* & ruby 1.9+
    scope :with_no_bars, -> { where(bars_count: 0) }
    scope :with_one_bar, -> { where(bars_count: 1) }
    scope :with_more_than_one_bar, -> { where("bars_count > 1") }
    

    That way you can save time counting bars for each foo every time you make such a request.

    I got this idea watching the railscast about counter cache: http://railscasts.com/episodes/23-counter-cache-column

    * What's new in Active Record [Rails 4 Countdown to 2013]

    0 讨论(0)
  • 2020-12-16 07:54
    class Foo < ActiveRecord::Base
      has_many :bars
    
      # I don't like having the number be part of the name, but you asked for it.
      named_scope :with_one_bar, :joins => :bars, :group => "bars.foo_id", :having => "count(bars.foo_id) = 1"
    
      # More generically...
      named_scope :with_n_bars, lambda {|n| {:joins => :bars, :group => "bars.foo_id", :having => ["count(bars.foo_id) = ?", n]}}
      named_scope :with_gt_n_bars, lambda {|n| {:joins => :bars, :group => "bars.foo_id", :having => ["count(bars.foo_id) > ?", n]}}
    
    end
    

    Called like so:

    Foo.with_n_bars(2)
    
    0 讨论(0)
提交回复
热议问题