如何返回一个空的ActiveRecord关系?

dza*_*jic 236 activerecord ruby-on-rails relation

如果我有一个带有lambda的范围并且它接受一个参数,取决于参数的值,我可能知道不会有任何匹配,但我仍然想要返回一个关系,而不是一个空数组:

scope :for_users, lambda { |users| users.any? ? where("user_id IN (?)", users.map(&:id).join(',')) : [] }
Run Code Online (Sandbox Code Playgroud)

我真正想要的是一种"无"方法,与"所有"相反,它返回一个仍然可以链接的关系,但会导致查询被短路.

ste*_*eh7 457

Rails 4中现在有一个"正确"的机制:

>> Model.none 
=> #<ActiveRecord::Relation []>
Run Code Online (Sandbox Code Playgroud)

  • 到目前为止,这还没有被移植到3.2或更早版本.只有边缘(4.0) (14认同)
  • 从Rails 4.0.5开始,`Model.none`不起作用.你需要使用一个*真正的*模型名称,例如`User.none`或者你有什么. (9认同)
  • @AugustinRiedinger`Model.scoped`做了你在rails 3中寻找的东西. (3认同)
  • 文档:http://api.rubyonrails.org/classes/ActiveRecord/QueryMethods.html#method-i-none (2认同)
  • 刚刚尝试使用Rails 4.0.5并且它正在运行.此功能使其成为Rails 4.0版本. (2认同)

ste*_*eh7 76

一个更便携的解决方案,不需要"id"列,并且不会假设不会有id为0的行:

scope :none, where("1 = 0")
Run Code Online (Sandbox Code Playgroud)

我还在寻找一种更"正确"的方式.

  • 是的,我真的很惊讶这些答案是我们最好的答案.我认为ActiveRecord/Arel仍然不太成熟.如果我不得不通过perambulations在Ruby中创建一个空数组,我真的很生气.基本上,这里也是一样的. (3认同)

Nat*_*ong 43

进入Rails 4

在Rails 4中,ActiveRecord::NullRelation可以从类似的调用返回一个可链接Post.none.

它或链式方法都不会生成对数据库的查询.

根据评论:

返回的ActiveRecord :: NullRelation继承自Relation并实现Null Object模式.它是一个具有已定义的null行为的对象,并且始终返回一个空的记录数组而不查询数据库.

查看源代码.


Bra*_*don 42

您可以添加名为"none"的范围:

scope :none, where(:id => nil).where("id IS NOT ?", nil)
Run Code Online (Sandbox Code Playgroud)

那会给你一个空的ActiveRecord :: Relation

您也可以在初始化程序中将它添加到ActiveRecord :: Base(如果需要):

class ActiveRecord::Base
 def self.none
   where(arel_table[:id].eq(nil).and(arel_table[:id].not_eq(nil)))
 end
end
Run Code Online (Sandbox Code Playgroud)

有很多方法可以获得这样的东西,但肯定不是保留在代码库中的最佳方法.我使用范围:无重构时发现我需要保证一个空的ActiveRecord :: Relation很短的时间.

  • `where('1 = 2')`可能更简洁一点 (14认同)
  • 如果你没有向下滚动到新的'正确'答案:`Model.none`是你如何做到这一点. (10认同)

小智 24

scope :none, limit(0)
Run Code Online (Sandbox Code Playgroud)

是一种危险的解决方案,因为您的范围可能会受到限制.

User.none.first

将返回第一个用户.它使用起来更安全

scope :none, where('1 = 0')
Run Code Online (Sandbox Code Playgroud)

  • 这个是正确的'范围:无,其中('1 = 0')'.如果你有分页,另一个会失败 (2认同)

Ale*_*lex 14

我认为我更喜欢这种方式与其他选项:

scope :none, limit(0)
Run Code Online (Sandbox Code Playgroud)

导致这样的事情:

scope :users, lambda { |ids| ids.present? ? where("user_id IN (?)", ids) : limit(0) }
Run Code Online (Sandbox Code Playgroud)

  • 请注意,如果在链中稍后调用`.first`或`.last`,则会覆盖`limit(0)`,因为Rails会将`LIMIT 1`追加到该查询中. (4认同)
  • @aceofspades where(false) 不起作用(Rails 3.0)但 where('false') 起作用。不是说你可能关心现在是 2013 年:) (2认同)