问题
我有以下ActiveRecord模型:
class Person belongs_to :favourite_car,class_name: 'Car' belongs_to :business_car,class_name: 'Car' belongs_to :home_car,class_name: 'Car' end
SELECT * FROM cars WHERE cars.id = ?
这基本上是N 1问题.
SELECT * FROM cars WHERE cars.id IN (?,?,?)
可能的方案
我可以把它移到has_many:through => :join_table与连接表中的列关联以指示关联类型是什么,然后使用includes([:join_table,:cars])来急切加载关联.但是,在这种情况下,它只将3个查询减少到2,并引入了一个额外的表.
另一种可能的方法
另一种可能的解决方案是手动加载关联,如下所示:
module EagerLoader def eager_load(*associations) reflections = associations.map { |association| self.class.reflections[association.to_sym] } raise 'Not all are valid associations' if reflections.any?(&:nil?) reflections.group_by { |association| association.klass }.each do |klass,reflections| load_associations(klass,reflections) end self end private def load_associations(klass,reflections) primary_key = klass.primary_key ids = reflections.map { |reflection| public_send(reflection.foreign_key) } records = klass.where(id: ids) reflections.each_with_index do |reflection,i| record = records.find do |record| record.public_send(primary_key) == ids[i] end public_send("#{reflection.name}=",record) end end end
我测试了它,它的工作原理.
class Person include EagerLoader end Person.find(2).eager_load(:favorite_car,:business_car,:home_car)
但是,当你想要做的事情时,这仍然无法帮助你
Person.includes(:favourite_car,:home_car)
解决方法
关于手动Eager加载有一篇很棒的帖子.
我想这就是你一直在寻找的东西:
owners = People.all association_name = :photos owners.each do |owner| record = whatever_you_want association = owner.association(association_name) association.target = record association.set_inverse_instance(record) end