【问题标题】:Retrieving a single record from an ActiveRecord/Rails query从 ActiveRecord/Rails 查询中检索单个记录
【发布时间】:2014-05-02 23:33:59
【问题描述】:

我发出如下查询,准确检索 0 或 1 条记录:

car = Car.where(vin: '1234567890abcdefg')

返回的当然是长度为 1 的汽车列表。所以我最终在查询末尾添加了.first

car = Car.where(vin: '1234567890abcdefg').first

似乎在 Rails 中应该有更好的方法。是否存在像 .onlyOne 这样的东西,它只会返回一个 Car,如果超过 1 个则抛出异常?

car = Car.where(vin: '1234567890abcdefg').onlyOne

car = Car.onlyOne(vin: '1234567890abcdefg')

【问题讨论】:

标签: ruby-on-rails activerecord ruby-on-rails-4 rails-activerecord


【解决方案1】:

导轨 4

您需要find_by(vin: 123),并且可以传递find_by 的任何字段/值名称的哈希值以供查找。

例如。

Car.find_by(vin: '123')

如果没有找到具有该 vin 的汽车或与该 vin 值匹配的单个汽车,这将返回 nil。如果您添加一个 bang(即find_by!),如果没有找到具有该 vin 的汽车,它将引发异常。

如果您想在发现多个错误时引发错误,那么您必须自己编写。如果您永远不应该遇到不止一辆汽车具有相同 vin 的情况,那么最好的方法是对 Car 模型进行唯一性验证,例如。

class Car < ActiveRecord::Base
  validates_uniqueness_of :vin # or validates :vin, unique: true
end

导轨 3

您想要find_by_vin(您可以将vin 替换为您的任何属性的名称。

例如。

Car.find_by_vin('123')

如果没有找到具有该 vin 的汽车或具有该 vin 值的单个汽车,这将返回 nil。如果您添加一个 bang(即find_by_vin!),如果没有找到具有该 vin 的汽车,它将引发异常。

欲了解更多信息:http://guides.rubyonrails.org/active_record_querying.html#retrieving-objects-from-the-database

【讨论】:

  • find_byfind_by! 正是我想要的!谢谢。
【解决方案2】:
car = Car.find_by_vin('1234567890abcdefg')

【讨论】:

  • 在这种情况下,它返回与该字符串匹配的所有记录,对吗?如果我错了,请纠正我。
  • 它只会返回一条记录。
  • @Pavan Car.find_all_by_vin('...') 将返回与字符串匹配的所有记录。
  • @Baldrick 谢谢你的澄清。
【解决方案3】:

我不认为有任何东西可以在有多个但在 rails 4 中抛出异常:

car = Car.find_by(vin: '1234567890abcdefg') 

只返回一个。强制唯一性的最佳方法是在模型/数据库级别上执行:

class Car < ActiveRecord::Base
  validates :vin, uniqueness: true
end

如果由于某种原因,当有不止一辆具有该 vin 的汽车时您确实需要抛出异常,您可以在模型(或猴子补丁活动记录)上编写一个方法:

class Car < ActiveRecord::Base
  class << self

    def only_one!(options)
       list = where(options)
       raise "Exactly one car isn't available" if list.size > 1 or list.size == 0
       list.first
    end

  end
end

或者使用关注点:

#lib/active_record_only_one.rb:
module ActiveRecordOnlyOne
  extend ActiveSupport::Concern

  module ClassMethods
    def only_one!(options)
      list = where(options)
      raise "Exactly one isn't available" if list.size > 1 or list.size == 0
      list.first
    end
  end
end

ActiveRecord::Base.send(:include, ActiveRecordOnlyOne)

.

#config/initializers/only_one.rb
require "active_record_only_one"

然后调用:

Car.only_one!(vuid: "1234567890abcdefg")

【讨论】:

  • 我刚刚意识到我不小心将onlyOne 使用了小驼峰大小写,而不是only_one 使用了蛇形大小写的Ruby 约定。
【解决方案4】:

您可以使用taketake!

car = Car.where(vin: '1234567890abcdefg').take

如果没有与给定字符串匹配的记录,则返回 nil

car = Car.where(vin: '1234567890abcdefg').take!

如果没有匹配给定字符串的记录,则返回ActiveRecord::RecordNotFound

来自Guides(1.1.2,1.1.5 and 1.1.6)

car = Car.take  is equivalent to

SELECT * FROM cars LIMIT 1

【讨论】:

    【解决方案5】:

    Rails 6.1 为此引入了find_sole_by(或where(...).sole)。

    似乎完全符合要求:

    查找唯一匹配的记录。如果未找到记录,则引发 ActiveRecord::RecordNotFound。如果找到多个记录,则引发 ActiveRecord::SoleRecordExceeded

    用例很简单:

    Car.find_sole_by(vin: '1234567890abcdefg')
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多