我正在尝试将ActiveRecord
模型中的数据与与每个对象相关的外部数据进行组合,并且我试图找出一种干净且高效的方法来完成此任务。
我显然可以在模型上定义一个实例方法来获取相应的外部数据:
class State < ActiveRecord::Base
def counties
@counties ||= fetch_external_data(state: name)['counties']
end
end
State.find_by_name('Alabama').counties
但是,如果我有一个对象集合(通过ActiveRecord::Relation
),那么这将导致对该外部数据源进行N + 1查询。
State.where(name: %w[Alabama Georgia]).map(&:counties)
理想情况下,我会急于从外部数据源加载所有数据,然后让counties
实例方法从此“共享”对象访问数据。我可以使用类方法来做到这一点:
class State < ActiveRecord::Base
attr_accessor :counties
def self.with_counties
# Get external data for all states in our scope
counties = fetch_external_data(states: pluck(:name))
# Group by the state
counties_by_state = counties.group_by { |c| c['state'] }
# Now hydrate all of the state models and set the counties
all.map do |state|
state.counties = counties_by_state.dig(state.name, 'counties')
end
end
end
虽然这是可能的,但它使我无法在使用State.where(name: %w[Alabama Georgia]).with_counties
之后进一步扩展作用域,并且感觉好像可以更优雅地处理。
是否有一种明确的方法可以像这样“急于加载”数据(即能够在ActiveRecord::Relation
对象上设置“收集数据”,当记录被水合后可以访问该对象)?如果我有一个代表该外部数据接口的类,是否可以在该类上定义一些方法以使其与State.includes(:counties)
或State.eager_load(:counties)
一起使用?
任何想法或建议将不胜感激。