【问题标题】:How can I get active record to throw an exception if there are too many records如果记录太多,如何获取活动记录以引发异常
【发布时间】:2019-09-08 22:28:42
【问题描述】:

遵循fail-fast的原则:

在查询应该只有一条记录的数据库时,如果.first() (first) 遇到多条记录,我想要一个异常。

我看到有一个 first! 方法会在记录少于预期的情况下抛出,但如果有两个或更多,我看不到任何内容。

如果记录多于预期,我如何让活动记录提前失败?

是否有原因导致活动记录不能以这种方式工作?

我习惯了 C# 的Single(),如果找到两条记录就会抛出。

【问题讨论】:

  • 验证这一点比引发错误更容易。如果这是一段关系,那么has_one 已经为您处理好了。
  • 很高兴知道,谢谢。在这种情况下,只有一个用于特定过滤器(范围),但还有更多不同的类型。

标签: ruby-on-rails activerecord


【解决方案1】:

如果有超过 1 条记录,为什么您会期望 activerecord 的 first 方法失败?以这种方式工作是没有意义的。

您可以定义自己的类方法,在获取第一个记录之前对记录进行计数。类似的东西

def self.first_and_only!
  raise "more than 1" if size > 1
  first!
end

如果有超过 1 个并且根本没有记录,则会引发错误。如果只有一个,它将返回它。

【讨论】:

  • 不错。如何在该方法中添加一个 bang 以表明它正在引发错误?另外,使用Ursus提到的one?方法怎么样? (比如,也许,raise "more than one" unless one?。)
  • 我想到了one? 方法,但我希望这会引发两个不同的错误,如果不止一个错误,如果没有错误。使用one? 当然是一种选择,但它会做一些稍微不同的事情。不过,这一切都取决于要求。是的,方法名称末尾的! 也是一个很好的做法,当然!。
  • 你可以使用 many? 然后代替例如raise "more than 1" if many?
  • @engineersmnky 嗯,看起来many? 方法在某些情况下使用to_a.many?,也许count > 1 只是为了计数更安全,但它可以通过apidock.com/rails/v4.2.7/ActiveRecord/Relation/many%3F 使用(检查源)。编辑:我猜size > 1 会更好
  • @arieljuod 不是你的情况。在您的情况下,它与count > 1 相同,因为在这种情况下sizecount
【解决方案2】:

如果您足够关心查询性能,则必须避免使用ActiveRecord::Relationcountone?none?many?any? 等,它们会产生 SQL select count(*) ... 查询。

所以,你可以使用 SQL limit 像:

def self.single!
  # Only one fast DB query
  result = limit(2).to_a
  # Array#many? not ActiveRecord::Calculations one
  raise TooManySomthError if result.many?
  # Array#first not ActiveRecord::FinderMethods one
  result.first
end

另外,当您希望只获得一条记录时,您必须使用 Relation 的 take 而不是 first。最后一个是真正的first记录,可以产生无用的SQLORDER BY

【讨论】:

  • @engineersmnky 很奇怪的问题。你可以在 1M 表上测试 select count(*) from table where conditionselect * from table where condition limit 2condition 满足,比如说,10+%。
  • @engineersmnky 唯一未损坏的 ActiveRecord::Relation 方法是 exists?take,当 many? 等在后台使用 count 时。他们这样做“只是因为” DRY:当关联被预加载时,count 鸭子类型为Array#count,而不是 - 到 SQL select count(*)
  • @engineersmnky 接受的答案有 2(!) 个 SQL 查询,即 >> 对象实例化。是的,2 个查询 + SQL count + SQL ORDER BY >>>> 一个额外的 AR 对象实例化(因为我们必须返回一个)。
  • @engineersmnky 无论如何,您可以在任何数据库大小上测试接受的答案时间与我的答案,这对于产品一来说意义重大。
【解决方案3】:

似乎 ActiveRecord 没有这样的方法。我发现一种有用的方法是one?,您可以在ActiveRecord::Relation 对象上调用它。你可以这样做

users = User.where(name: "foo")
raise StandardError unless users.one?

也许还可以定义你自己的自定义异常

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2019-03-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-12-21
    • 2014-02-28
    • 2013-11-11
    相关资源
    最近更新 更多