预加载条件lambda失败的关联

时间:2014-06-23 20:49:31

标签: ruby-on-rails ruby-on-rails-4

我在尝试预加载包含条件lambda的has_many关联时遇到NoMethodError。

以下是我的模特

Class Event < ActiveRecord::Base
  belongs_to :user
end

Class CalendarDay < ActiveRecord::Base
  belongs_to :user

  has_many :events, 
    ->(_day) { 
      where(
        user_id: _day.user_id, 
        deleted_at: nil
      ) 
    },
    autosave: false,
    foreign_key: :date,
    primary_key: :date, 
end

加载一些CalendarDays并尝试预加载事件。

days = CalendarDay.all.to_a

loader = ActiveRecord::Associations::Preloader.new
loader.preload(days, :events)

#NoMethodError: undefined method `user_id' for nil:NilClass

预加载器似乎只适用于没有条件lambda的关系。

有人有什么想法吗?这是ActiveRecord :: Associations :: Preloader类中的错误吗?

2 个答案:

答案 0 :(得分:1)

如果要预加载关联,则可以使用条件,但是不会传递父记录(在本例中为_day),因为每个日期对象都会有所不同。

Activerecord需要提出一个查询来加载所有事件,并且当每个父对象的条件发生变化时,它不知道如何做到这一点(如果我的内存是,当你包含一个关联时也是如此)正确的)

答案 1 :(得分:0)

我喜欢一篇描述如何手动预加载的好文章。 http://mrbrdo.wordpress.com/2013/09/25/manually-preloading-associations-in-rails-using-custom-scopessql/

这是我的预加载代码。

collection = CalendarDay.all
dates      = collection.map(&:date)
user_ids   = collection.map(&:user_id)

events = Event.where(
  date: dates,
  user_id: user_ids
).to_a

collection.each do |record|
  association = record.association(:events)
  association.loaded!
  association.target.concat(

    #events.where(
    #  user_id: record.user_id,
    #  date: record.date,
    #  deleted_at: nil
    #)

    events.find_all { |e|
      e.user_id    == record.user_id &&
      e.date       == record.date &&
      e.deleted_at == nil
    }

  )
end

结果

#No preload code.  
Views: 1129.8ms | ActiveRecord: 409.8ms

#Cache active record relation then query for each day.
Views: 296.4ms | ActiveRecord: 198.6ms

#Run query and use find_by
Views: 290.2ms | ActiveRecord: 28.2ms