【问题标题】:How do I get back an ActiveRecord instead of an ActiveRecord relation?如何取回 ActiveRecord 而不是 ActiveRecord 关系?
【发布时间】:2017-02-10 02:08:08
【问题描述】:

使用 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">]>

【问题讨论】:

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


    【解决方案1】:

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

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

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

    【讨论】:

    • 我编辑了我的问题以显示当我尝试您的建议时发生了什么。只需添加“.take”?它不工作。
    • Nvm,它确实有效——数据正在被缓存,但是当我退出控制台并重新加载它时运行良好。
    【解决方案2】:

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

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

    返回一个ActiveRecord::Relation。当您在ActiveRecord 中使用where 子句时,总是会出现这种情况。为了获得特定的ActiveRecord 对象,您可以:

    • 使用find_by 方法返回它找到的数据库中的第一个实例。请注意,这不会返回与条件匹配的所有记录。 * 例如,User.find_by(first_name: "Mark") 将返回一个 ActiveRecord 对象,其中包含名字为“Mark”的第一条记录
    • ActiveRecord::Relation 集合使用first 方法。例如,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
      

    【讨论】:

      【解决方案3】:

      大多数 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

      还有这个(Querying 将它的大部分方法委托给它):https://github.com/rails/rails/blob/603ebae2687a0b50fcf003cb830c57f82fb795b9/activerecord/lib/active_record/querying.rb

      可能值得略读。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2011-06-20
        • 1970-01-01
        • 2011-07-18
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-04-11
        相关资源
        最近更新 更多