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

Posted

技术标签:

【中文标题】如何返回一个空的 ActiveRecord 关系?【英文标题】:How to return an empty ActiveRecord relation? 【发布时间】:2011-06-20 03:50:56 【问题描述】:

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

scope :for_users, lambda  |users| users.any? ? where("user_id IN (?)", users.map(&:id).join(',')) : [] 

我真正想要的是一个“none”方法,与“all”相反,它返回一个仍然可以链接的关系,但会导致查询被短路。

【问题讨论】:

如果你只是让查询,运行它会返回一个关系:User.where('id in (?)', []).class=> ActiveRecord::Relation.您是否试图完全避免查询? 正确。如果我知道不可能有任何匹配项,理想情况下,可以完全避免查询。我只是将它添加到 ActiveRecord::Base: "def self.none; where(:id => 0); end" 似乎可以满足我的需要。 > 您是否要完全避免查询?完全有道理,有点蹩脚,我们需要为此打 DB 【参考方案1】:

Rails 4 现在有一个“正确”的机制:

>> Model.none 
=> #<ActiveRecord::Relation []>

【讨论】:

到目前为止,这还没有被移植到 3.2 或更早版本。仅边缘 (4.0) 文档:api.rubyonrails.org/classes/ActiveRecord/… 刚刚尝试使用 Rails 4.0.5 并且它正在工作。此功能已在 Rails 4.0 版本中发布。 @AugustinRiedinger Model.scoped 可以满足您在 Rails 3 中的需求。 从 Rails 4.0.5 开始,Model.none 不起作用。您需要使用您的 real 模型名称之一,例如User.none 或你有什么。【参考方案2】:

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

scope :none, where("1 = 0")

我还在寻找更“正确”的方法。

【讨论】:

是的,我真的很惊讶这些答案是我们所拥有的最好的。我认为 ActiveRecord/Arel 一定还很不成熟。如果我必须通过遍历来在 Ruby 中创建一个空数组,我会非常恼火。基本上也是一样的。 虽然有点老套,但这 Rails 3.2的正确方法。对于 Rails 4,请参阅@steveh7 的其他答案:***.com/a/10001043/307308 scope :none, -&gt; where("false") 【参考方案3】:

加入 Rails 4

在 Rails 4 中,可链接的 ActiveRecord::NullRelation 将从 Post.none 之类的调用中返回。

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

根据cmets:

返回的 ActiveRecord::NullRelation 继承自 关系并实现空对象模式。它是一个对象 定义空行为并始终返回一个空的记录数组 无需查询数据库。

请参阅source code。

【讨论】:

【参考方案4】:

您可以添加一个名为“none”的范围:

scope :none, where(:id => nil).where("id IS NOT ?", nil)

这会给你一个空的 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

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

【讨论】:

where('1=2') 可能更简洁一些 如果您不向下滚动到新的“正确”答案:Model.none 就是您这样做的方式。【参考方案5】:
scope :none, limit(0)

这是一个危险的解决方案,因为您的范围可能会被束缚。

User.none.first

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

scope :none, where('1 = 0')

【讨论】:

这个是正确的 'scope :none, where('1 = 0')'。如果您有分页,另一个将失败【参考方案6】:

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

scope :none, limit(0)

导致这样的事情:

scope :users, lambda  |ids| ids.present? ? where("user_id IN (?)", ids) : limit(0) 

【讨论】:

我更喜欢这个。我不知道为什么where(false) 不能完成这项工作——它使范围保持不变。 注意limit(0) 将被覆盖,如果您在链的后面调用.first.last,因为Rails 会将LIMIT 1 附加到该查询。 @aceofspades where(false) 不起作用(Rails 3.0)但 where('false') 起作用。并不是说你现在可能关心它是 2013 年 :) 谢谢@Ritchie,从那以后我想我们也有none 关系,如下所述。【参考方案7】:

使用作用域:

范围 :for_users, lambda |users| users.any? ? where("user_id IN (?)", users.map(&:id).join(',')) : scoped

但是,您也可以通过以下方式简化代码:

范围 :for_users, lambda |users| where(:user_id => users.map(&:id)) 如果 users.any?

如果你想要一个空的结果,使用这个(去掉 if 条件):

范围 :for_users, lambda |users|其中(:user_id => users.map(&:id))

【讨论】:

返回“scoped”或nil并没有达到我想要的效果,也就是将结果限制为零。返回“作用域”或 nil 对作用域没有影响(这在某些情况下很有用,但在我的情况下没有用)。我想出了自己的答案(见上面的 cmets)。 我添加了一个简单的解决方案让您也返回一个空结果:)【参考方案8】:

也有变种,但都是向db发出请求

where('false')
where('null')

【讨论】:

BTW 请注意,这些必须是字符串。 where(false)where(nil) 会被忽略。【参考方案9】:

这是可能的,所以是这样的:

scope :for_users, lambda  |users| users.any? ? where("user_id IN (?)", users.map(&:id).join(',')) : User.none 

http://apidock.com/rails/v4.0.2/ActiveRecord/QueryMethods/none

如果我错了,请纠正我。

【讨论】:

以上是关于如何返回一个空的 ActiveRecord 关系?的主要内容,如果未能解决你的问题,请参考以下文章

如何使用has_many通过关系缩短三重连接ActiveRecord查询?

如何在ActiveRecord的关系表中使用枚举?

如何引发 ActiveRecord::Rollback 异常并一起返回值?

Laravel 与 uuid 的多对多关系返回总是空的

ActiveRecord Rails 3 范围与类方法

Rails Activerecord 关系:使用子查询作为 SQL 选择语句的表