ruby-on-rails – ActiveRecord渴望加载多个belongs_to关联

前端之家收集整理的这篇文章主要介绍了ruby-on-rails – ActiveRecord渴望加载多个belongs_to关联前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
问题

我有以下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)

例如,在人员索引页面上.这会将查询数量从3N减少到4,但实际上只需要2个.

有没有更好的解决方案来解决这个问题?

解决方法

关于手动Eager加载有一篇很棒的帖子.

http://mrbrdo.wordpress.com/2013/09/25/manually-preloading-associations-in-rails-using-custom-scopessql/

我想这就是你一直在寻找的东西:

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

猜你在找的Ruby相关文章