【问题标题】:Using named_scope with counts of child models将 named_scope 与子模型计数一起使用
【发布时间】:2010-05-24 20:18:41
【问题描述】:

我有一个简单的父对象,它有很多子对象。我试图弄清楚如何使用命名范围来只带回具有特定数量孩子的父母。

这可能吗?

class Foo < ActiveRecord::Base
    has_many :bars
    named_scope :with_no_bars, ... # count of bars == 0
    named_scope :with_one_bar, ... # count of bars == 1
    named_scope :with_more_than_one_bar, ... # count of bars > 1
end

class Bar < ActiveRecord::Base
    belongs_to :foo
end

我希望做类似Foo.with_one_bar

我可以为这样的事情在父类上编写方法,但我宁愿拥有命名作用域的力量

【问题讨论】:

    标签: ruby-on-rails activerecord ruby-on-rails-2 named-scope


    【解决方案1】:
    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
    

    这样称呼:

    Foo.with_n_bars(2)
    

    【讨论】:

    • 总的来说,我同意这个名字,我通常会把它作为一个概括并传递一个参数,就像你下面的例子一样,但在详细的情况下,名字很重要。使用计数只有一个问题。 Foo.with_one_bar.count 会夸大答案。 .size 工作正常,我可以futz 和其余的工作。非常感谢您的帮助!
    • 这对我有帮助。谢谢!
    • 你对“with_no_bars”有什么建议吗?
    • 对于范围with_no_barsstackoverflow.com/questions/18082096/…
    【解决方案2】:

    我会为此使用counter cache。因此,您需要进行以下迁移:

    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
    

    你也需要像这样改变Bar 模型:

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

    现在bars 的计数缓存在foo 模型中,这将加快您对bars 计数的查询。

    你的 named_scopes 也看起来像这样:

    #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") }
    

    这样,您每次提出此类请求时都可以节省为每个 foo 计算 bars 的时间。

    我在观看有关计数器缓存的 railscast 时产生了这个想法:http://railscasts.com/episodes/23-counter-cache-column

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

    【讨论】:

    • 感谢 Jens,非常酷的解决方案,我也会检查一下 Railscast。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-09-28
    • 2012-10-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-08-20
    相关资源
    最近更新 更多