2

我有两个这样的模型:

class Country < ActiveRecord::Base
   has_many :cities
end

class City < ActiveRecord::Base
   belongs_to :country
   scope :big, where("population > 1000000")
end

然后,在代码中,我加载了一个国家的城市,如下所示:

country = Country.include(:cities).find(id)

但是当我执行时:

country.cities.big

它通过以下查询对数据库产生了影响:

SELECT * FROM cities where country_id = 1 AND population > 1000000

哪个工作正常,但是没有必要,因为所有城市都已经被 :include 加载了。如果关联已经加载,有没有办法告诉范围不要命中数据库?

我可以使用关联扩展来做到这一点,但不能用于常规范围。在扩展我做类似的事情:

has_many :cities do
   def big
      if loaded?
        detect {|city| city.population > 1000000}
      else
        where("population > 1000000")
      end
   end
end

但这将在 2 个地方重复范围,我想在城市模型上重用范围。

4

1 回答 1

1

范围逻辑使用与 Arel 一起工作的方法,而 ruby​​ Enumerables 不知道如何使用它们。您也许可以将您的逻辑重构为可以转换为使用 Arel 或 Enumerable 方法的抽象,但这并不总是可能的:

def self.build_scope(abstracted)
  where(abstracted.map(&:to_s).join(' '))
end

def self.build_enum(abstracted)
  select{|city| city.send(abstracted[0]).send(*abstracted[1..2]) }
end

def self.abstract_big
  [:population, ">", 10000]
end

scope :big_scope, build_scope(abstract_big)

def self.big_enum
  build_enum abstract_big      
end

然后你可以这样做:

country.cities.big_enum

一个更好的主意是只根据您想要的范围急切地加载(如果您事先知道的话):

country = Country.include(:cities).merge(City.big).find(id)
于 2012-12-21T15:01:14.787 回答