如何取回ACtiveRecord而不是ACtiveRecord关系?

时间:2017-02-10 02:08:09

标签: ruby-on-rails ruby activerecord model ruby-on-rails-5

使用Rails 5.如何获取ActiveRecord而不是ActiveRecord关系?我是下面的模型

class State < ActiveRecord::Base
  belongs_to :country

    ...
  def self.cached_find_us_state_by_name(name)
    Rails.cache.fetch("#{name.upcase} US") do
      find_us_state_by_name(name)
    end
  end

  # Look up a US state by its full name 
  def self.find_us_state_by_name(name)
    search_criteria = ["upper(states.name) = ? "]
    search_criteria.push( "states.country_id = countries.id " ) 
    search_criteria.push( "countries.iso = 'US' " ) 

    results = State.joins(:country).where( search_criteria.join(' and '),
                                           name.upcase) 
  end

但是当我使用这些方法查找项目时,我回来的是一个ActiveReocrd关系......

2.4.0 :004 >  State.cached_find_us_state_by_name('Ohio')
 => #<ActiveRecord::Relation [#<State id: 3538, name: "Ohio", country_id: 233, iso: "OH">]>

这是后来的问题,特别是......

ActiveRecord::AssociationTypeMismatch: State(#70288305660760) expected, got State::ActiveRecord_Relation(#70288290686360)
    from /Users/davea/.rvm/gems/ruby-2.4.0@global/gems/activerecord-5.0.1/lib/active_record/associations/association.rb:221:in `raise_on_type_mismatch!'
    from /Users/davea/.rvm/gems/ruby-2.4.0@global/gems/activerecord-5.0.1/lib/active_record/associations/belongs_to_association.rb:12:in `replace'

编辑:根据给出的建议,我将方法改为

  def self.cached_find_us_state_by_name(name)
    Rails.cache.fetch("#{name.upcase} US") do
      find_us_state_by_name(name)
    end
  end

  # Look up a US state by its full name 
  def self.find_us_state_by_name(name)
    search_criteria = ["upper(states.name) = ? "]
    search_criteria.push( "states.country_id = countries.id " ) 
    search_criteria.push( "countries.iso = 'US' " ) 

    results = State.joins(:country).where( search_criteria.join(' and '),
                                           name.upcase).take  
  end

但是,我仍然得到一个ActiveRecord :: Relation

2.4.0 :011 > State.cached_find_us_state_by_name('Ohio')
 => #<ActiveRecord::Relation [#<State id: 3538, name: "Ohio", country_id: 233, iso: "OH">]>

3 个答案:

答案 0 :(得分:0)

您可以使用find_by代替where - 源代码很简单:

def find_by(*args)
  where(*args).take
end

另一种方法是拨打.first[0],但如果您的where有大量记录,这将会很慢,因为它会在选择之前将它们全部加载到内存中第一。如果使用limit(1),那么这些方法将是可接受的快速。

答案 1 :(得分:0)

有多种方法可以从ActiveRecord获取ActiveRecord::Relation对象。调用

的结果
results = State.joins(:country).where( search_criteria.join(' and '),
                                       name.upcase) 

返回ActiveRecord::Relation。当您在where中使用ActiveRecord子句时,情况总是如此。为了获得特定的ActiveRecord对象,您可以:

  • 使用find_by方法返回它找到的数据库中的第一个实例。请注意,这不会返回符合条件的所有记录。 *例如,User.find_by(first_name: "Mark")将返回一个ActiveRecord对象,其中包含名为&#34; Mark&#34;
  • 的第一个记录
  • 使用first集合上的ActiveRecord::Relation方法。例如,User.where(first_name: "Mark").first与前一个示例的作用相同。
  • 最后,你可以这样写:

    User.where(first_name: "Mark") do |u|
      puts u.last_name
    end
    

    这允许您一次遍历ActiveRecord::Relation集合一条记录。因此,对于您的示例,它看起来像这样:

    results.each do |res|
      # do stuff
    end
    

答案 2 :(得分:0)

大多数ActiveRecord方法返回ActiveRecord :: Relations,以便提供流畅的API(如果你愿意的话,语法上令人愉悦的方法链接)。我认为这是一个设计决定。有一些方法可以解决此问题,例如first(实际上是对find_nth!(0)的调用),last to_ato_sql以及其他一些方法。

所以,我发现“周围”的方式就是使用.first我真正想要得到一个而且只有一个结果,正如Mark和maxple所暗示的那样。我也认为这可能是唯一的方法之一。

这:https://github.com/rails/rails/blob/603ebae2687a0b50fcf003cb830c57f82fb795b9/activerecord/lib/active_record/querying.rb

这个(查询代表大部分方法):https://github.com/rails/rails/blob/603ebae2687a0b50fcf003cb830c57f82fb795b9/activerecord/lib/active_record/querying.rb

可能值得一试。