映射在活动记录中的范围

时间:2014-06-13 08:41:27

标签: ruby activerecord padrino

我有一个活动记录,我有一个用例:我想在map ActiveRecord中使用scopes。以下是我的代码:

class SupportedGeoIdAndLocation < ActiveRecord::Base

   scope :supported_geo_ids,            lambda { all.map(&:geo_id).uniq }
   # also tried: scope :supported_geo_ids,            lambda { select('distinct geo_id').map(&:geo_id).uniq }
   scope :locations_for_geo_id, lambda { |geo_id| where(:geo_id => geo_id).map(&:location) }
end

但当我SupportedGeoIdAndLocation.supported_geo_ids时,我得到空数组,而SupportedGeoIdAndLocation.all.map(&:geo_id).uniq给出了我想要的结果。

我正在寻找一种方法,以便可以一次性使用lambda完成,而不是通过链接不同的方法并完全避免类函数。

看起来我正在以错误的方式使用示波器,请建议我可以采用哪种方法。

版本:padrino:0.10.5,ruby:ruby-1.9.3-p429,ActiveRecord:3.1.6

3 个答案:

答案 0 :(得分:5)

scope应返回ActiveRecord::Relation,因此范围可以与其他范围或类方法等链接。

如果你想获得一个数组,请改用class方法。

def self.supported_geo_ids
  all.map(&:geo_id).uniq
end

答案 1 :(得分:3)

在我的一个模型中使用了一些类似的代码后,我终于找到this question(因为我得到了相同的错误消息(在Rails 3.2中))。接受的答案指出ActiveRecord隐含地假定scopes是&#34;可链接的&#34; (在ARel意义上)你返回的数组显然不是。

因此,虽然您可以在类方法中执行任何您想要的操作(如@xdazz的答案),但如果您坚持使用ActiveRecord,则必须遵守scope约定(乍一看只是用你提供的lambda来定义一个类方法。似乎还有比我能发现的更多。

<强>更新

经过一些更多的摆弄后,我觉得我得到了尽可能接近。您可以编写一个只返回支持的地理ID的scope,如下所示:

class SupportedGeoIdAndLocation < ActiveRecord::Base
  scope :supported_geo_ids,  lambda { select("distinct(geo_id)") }
end

然而,这将返回一个ARel关系,该关系可以转换为只有SupportedGeoIdAndLocation属性集的geo_id个对象数组(抱歉让您失望, 是一个类方法,即使它最终是通过scope类方法定义的。因此,如果你需要一个属性数组,你可以使用你在另一个类方法中提出的方法将它变成一个:

class SupportedGeoIdAndLocation < ActiveRecord::Base
  def self.geo_ids_array
    supported_geo_ids.map(&:geo_id)
  end
end

请注意,现在无需致电uniq,因为distinct中的scope已对此进行了处理。从rails 4.0.2开始ActiveRecord确实支持distinct方法,因此如果您可以更新ActiveRecord gem,则不必使用字符串Padrino不应该依赖它。

答案 2 :(得分:3)

正如@patru指出的那样,范围只是为了返回关系。它们并不意味着返回价值。因此,您的问题&#34;如何使范围返回特定值?&#34;无法回答。

我的印象是你觉得类方法设计不好,或者没有&#34; The Rails Way&#34;,但在这种情况下,我同意@xdazz他们是最好的解决方案。

但是,我会稍微改变一下这个方法:

def self.supported_geo_ids
  self.all(:select => "DISTINCT(geo_id)").map(&:geo_id)
end

这样,在数据库引擎中执行重复数据删除,并且仅返回所需的值。这使得查询可能更快。

在较新版本的Rails中,您可以这样写:

def self.supported_geo_ids
  self.uniq.pluck(:geo_id)
end

如果由于某种原因你仍然不想使用课堂方法,请解释原因,我会尝试考虑解决方法。