在模型中急切加载?

时间:2013-04-21 16:36:49

标签: ruby-on-rails

我想知道我们是否可以在模型级别中加载:

# country.rb
class Country < ActiveRecord::Base
  has_many :country_days

  def country_highlights
    country_days.map { |country_day| country_day.shops }.flatten.uniq.map { |shop| shop.name }.join(", ")
  end
end

# country_day.rb
class CountryDay < ActiveRecord::Base
  belongs_to :country
  has_many :country_day_shops
  has_many :shops, :through => :country_day_shops
end

# shop.rb
class Shop < ActiveRecord::Base    
end

由于某些多态关联,大多数时候在控制器中使用.includes很困难。无论如何,我是否急于在模型级别加载方法country_highlights,这样我就不必在控制器中添加.includes了?

2 个答案:

答案 0 :(得分:2)

您不能从模型实例“急切加载”country_days,但您当然可以使用has_many through:跳过将它们全部加载到一起。您也可以跳过额外的地图。

# country.rb
class Country < ActiveRecord::Base
  has_many :country_days
  has_many :country_day_shops, through: :country_days  #EDIT: You may have to add this relationship
  has_many :shops, through: :country_day_shops #And change this one to use the new relationship above.

  def country_highlights
    shops.distinct_names.join(", ")
  end
end

# country_day.rb
class CountryDay < ActiveRecord::Base
  belongs_to :country
  has_many :country_day_shops
  has_many :shops, :through => :country_day_shops
end

# shop.rb
class Shop < ActiveRecord::Base
  def self.distinct_names
    pluck("DISTINCT shops.name")  #Edit 2: You may need this instead of 'DISTINCT name' if you get an ambiguous column name error.
  end
end

has_many through:将使用JOIN加载关联商店记录,实际上是急切加载它们,而不是加载所有country_day记录,然后为每个country_day记录加载相关商店。

pluck("DISTINCT name")将返回数据库中所有商店唯一名称的数组,使用数据库执行SELECT DISTINCT,因此它不会返回重复记录,{{1}当您需要的只是字符串pluck时,将避免加载ActiveRecord实例。

答案 1 :(得分:1)

编辑:首先阅读评论

您可以缓存最终结果(在您的情况下加入的字符串或文本记录),因此您不必加载几个级别的记录来构建此结果。

1)添加country_highlights文本列(结果可能超出字符串列限制)

2)使用回调缓存模型中的country_highlights,例如在每次保存之前。

class Country < ActiveRecord::Base  
  has_many :country_days

  before_save :cache_country_highlights

  private

  def cache_country_highlights
    self.country_highlights = country_days.flat_map(&:shops).uniq.map(&:name).join(", ")
  end
end

缓存计算会在保存记录时调用一些开销,但是只需要加载一个而不是三个模型记录进行显示,这样可以加快控制器的操作速度,这是值得的。