是否有可能否定Rails 3中的范围?

时间:2011-08-14 00:30:58

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

我的班级名为Collection

scope :with_missing_coins, joins(:coins).where("coins.is_missing = ?", true)

我可以运行Collection.with_missing_coins.count并获得结果 - 效果很好! 目前,如果我想获得不丢失硬币的收藏品,我会添加另一个范围:

scope :without_missing_coins, joins(:coins).where("coins.is_missing = ?", false)

我发现自己写了很多这些“相反”的范围。是否有可能在不牺牲可读性或使用lambda /方法(以truefalse作为参数)的情况下获得范围的反面?

这样的事情:

Collection.!with_missing_coins

6 个答案:

答案 0 :(得分:49)

在Rails 4.2中,你可以这样做:

scope :original, -> { ... original scope definition ... }
scope :not_original, -> { where.not(id: original) }

它将使用子查询。

答案 1 :(得分:11)

我不会为此使用单个范围,而是两个:

scope :with_missing_coins, joins(:coins).where("coins.is_missing = ?", true)
scope :without_missing_coins, joins(:coins).where("coins.is_missing = ?", false)

这样,当使用这些范围时,它就会明确地发生了什么。有了数字1311407建议的数字,falsewith_missing_coins的{​​{1}}参数的作用并不是很清楚。

我们应该尝试尽可能清晰地编写代码,如果这意味着不再是关于DRY的狂热者,那么就这样吧。

答案 2 :(得分:7)

范围本身并没有“逆转”,尽管我不认为使用lambda方法是一个问题。

scope :missing_coins, lambda {|status| 
  joins(:coins).where("coins.is_missing = ?", status) 
}

# you could still implement your other scopes, but using the first
scope :with_missing_coins,    lambda { missing_coins(true) }
scope :without_missing_coins, lambda { missing_coins(false) }

然后:

Collection.with_missing_coins
Collection.without_missing_coins

答案 3 :(得分:0)

这可能只是行得通,并没有进行太多测试。使用rails 5我猜想rails 3具有where_values方法,而不是where_clause。

        scope :not, ->(scope_name) { query = self
      send(scope_name).joins_values.each do |join|
      query = query.joins(join)
    end
                                    query.where((send(scope_name).where_clause.send(:predicates).reduce(:and).not))}

用法

Model.not(:scope_name)

答案 4 :(得分:0)

@bill-lipa 的回答很好,但是当您想要的范围涉及关联或附件时要小心。

如果我们有以下范围选择所有附有简历的用户:

scope :with_resume, -> { joins(:resume_attachment) } 

如果 with_resume 为空,以下否定将导致异常:

scope :without_resume, -> { where.not(id: with_resume) } 
#=> users.without_resume
#=> ActiveRecord::StatementInvalid (PG::InvalidColumnReference: ERROR:  for SELECT DISTINCT, ORDER BY expressions must appear in select list

因此,您需要先检查以获得所需的结果:

scope :without_resume, -> { with_resume.any? ? where.not(id: with_resume) : where.not(id: []) }

答案 5 :(得分:-2)

更新。现在,Rails 6添加了方便又漂亮的负枚举方法。

# All inactive devices
# Before
Device.where.not(status: :active)
#After 
Device.not_active

博客文章 here