新手问题 关于 eager loading 部分的 Nested Associations

Awlter1 · 2018年01月31日 · 最后由 Awlter1 回复于 2018年02月01日 · 1098 次阅读

http://guides.rubyonrails.org/v4.1/active_record_querying.html#eager-loading-multiple-associations

我的 model 之间的关联是这样的

class Video < ActiveRecord::Base
  belongs_to :category
  has_many :reviews, -> { order(created_at: :desc) }
  has_many :queue_items
end

class QueueItem < ActiveRecord::Base
  belongs_to :video
  belongs_to :user
end

class Category < ActiveRecord::Base
  has_many :videos, -> { order(created_at: :desc) }
end

为什么在有 category 参与的情况下的查询都会返回一个 Class:#<QueueItem::ActiveRecord_Relation, 这个不能拿来用啊,@queue_items[0]会报错NoMethodError: undefined method 'each' for nil:NilClass。只 includes reviews 就没问题。请看:


    4: def index
    5:   @queue_items = QueueItem.includes(video: ['reviews', 'category'])
 => 6:   binding.pry
    7: end

[1] pry(#<QueueItemsController>)> @queue_items
  QueueItem Load (0.3ms)  SELECT "queue_items".* FROM "queue_items"
  Video Load (0.2ms)  SELECT "videos".* FROM "videos"  WHERE "videos"."id" IN (8, 9, 1)
  Review Load (0.3ms)  SELECT "reviews".* FROM "reviews"  WHERE "reviews"."video_id" IN (1, 8, 9)  ORDER BY "reviews"."created_at" DESC
  Category Load (0.2ms)  SELECT "categories".* FROM "categories"  WHERE "categories"."id" IN (2, 1)
=> #<#<Class:#<QueueItem::ActiveRecord_Relation:0x007fce43081600>>:0x3fe721840b00>

#只load reviews的话就没问题
[2] pry(#<QueueItemsController>)> @queue_items = QueueItem.includes(video: ['reviews'])
  CACHE (0.0ms)  SELECT "queue_items".* FROM "queue_items"
  CACHE (0.0ms)  SELECT "videos".* FROM "videos"  WHERE "videos"."id" IN (8, 9, 1)
  CACHE (0.0ms)  SELECT "reviews".* FROM "reviews"  WHERE "reviews"."video_id" IN (1, 8, 9)  ORDER BY "reviews"."created_at" DESC
=> [#<QueueItem id: 1, position: 2, user_id: 1, video_id: 8>,
 #<QueueItem id: 2, position: 1, user_id: 1, video_id: 9>,
 #<QueueItem id: 3, position: 1, user_id: 2, video_id: 8>,
 #<QueueItem id: 5, position: 3, user_id: 1, video_id: 1>]


[3] pry(#<QueueItemsController>)> @queue_items = QueueItem.includes(video: ['category'])
  CACHE (0.0ms)  SELECT "queue_items".* FROM "queue_items"
  CACHE (0.0ms)  SELECT "videos".* FROM "videos"  WHERE "videos"."id" IN (8, 9, 1)
  CACHE (0.0ms)  SELECT "categories".* FROM "categories"  WHERE "categories"."id" IN (2, 1)
=> #<#<Class:#<QueueItem::ActiveRecord_Relation:0x007fce3f5c5480>>:0x3fe71fae2a40>
[4] pry(#<QueueItemsController>)> @queue_items[0]
  CACHE (0.0ms)  SELECT "queue_items".* FROM "queue_items"
  CACHE (0.0ms)  SELECT "videos".* FROM "videos"  WHERE "videos"."id" IN (8, 9, 1)
  CACHE (0.0ms)  SELECT "categories".* FROM "categories"  WHERE "categories"."id" IN (2, 1)
NoMethodError: undefined method `each' for nil:NilClass
from /Users/wangxuefei/.rvm/gems/ruby-2.1.2@myflix/gems/activerecord-4.1.1/lib/active_record/associations/preloader/association.rb:87:in `block in associated_records_by_owner'

到底是哪里出错了呢?

劳烦各位啦

我已经 google 过了,在 google 上都没人问过这个问题。。

class QueueItem < ActiveRecord::Base
  belongs_to :video
  belongs_to :user

  validates :position, numericality: { only_integer: true }

  def video_title
    video.title
  end

  def video_category
    video.category.name
  end

关联应该是没问题的,因为video_category这个方法是可以用的

@queue_items = QueueItem.includes(video: ['category']) 后边调试的时候 @queue_items.to_a 看看出什么错,就有更进一步的信息了

jasl 回复
[4] pry(#<QueueItemsController>)> @queue_items[0]
  CACHE (0.0ms)  SELECT "queue_items".* FROM "queue_items"
  CACHE (0.0ms)  SELECT "videos".* FROM "videos"  WHERE "videos"."id" IN (8, 9, 1)
  CACHE (0.0ms)  SELECT "categories".* FROM "categories"  WHERE "categories"."id" IN (2, 1)
NoMethodError: undefined method `each' for nil:NilClass
from /Users/wang/.rvm/gems/ruby-2.1.2@myflix/gems/activerecord-4.1.1/lib/active_record/associations/preloader/association.rb:87:in `block in associated_records_by_owner'

我试过了,上面是我问题里的代码,下次问问题的时候再明确些指出来:)

Awlter1 回复

那你的意思是 to_a 返回了 [] 而不是报错?我没记错这个 [] 的实现是代理给 CollectionProxy 内部的对象,也就只有结果集没有正确加载的时候才可能出现你描述的这个问题

jasl 回复
From: /Users/wangxuefei/Documents/launchschool/rails_review/app/controllers/queue_items_controller.rb @ line 6 QueueItemsController#index:

    4: def index
    5:   @queue_items = current_user.queue_items.includes(video: ['category'])
 => 6:   binding.pry
    7: end

[1] pry(#<QueueItemsController>)> @queue_items.to_a
  QueueItem Load (0.3ms)  SELECT "queue_items".* FROM "queue_items"  WHERE "queue_items"."user_id" = $1  ORDER BY "queue_items"."position" ASC  [["user_id", 1]]
  Video Load (0.2ms)  SELECT "videos".* FROM "videos"  WHERE "videos"."id" IN (8, 4, 1)
  Category Load (0.2ms)  SELECT "categories".* FROM "categories"  WHERE "categories"."id" IN (2, 1)
NoMethodError: undefined method `each' for nil:NilClass
from /Users/wangxuefei/.rvm/gems/ruby-2.1.2@myflix/gems/activerecord-4.1.1/lib/active_record/associations/preloader/association.rb:87:in `block in associated_records_by_owner'

to_a也是一样报错NoMethodError: undefined methodeach' for nil:NilClass`

jasl 回复

没,我的 “试过了” 的意思是我觉得@queue_items.to_a@queue_items[0]的试错效果是一样的,刚刚试了下的确一样(上面的代码)

Awlter1 回复

那么很可能是 Rails 自己的 bug 了,github 上已经看不了 4.1.1 的源码了,你试试升级到 4.1 的最终版本 4.1.16,可以直接升级,看看是否解决,如果没有的话,感觉就很难办了

jasl 回复

我知道我没犯错就行🤓。现在不敢升到 4.1.16,等有机会试一下,谢大神:)

Awlter1 关闭了讨论 02月01日 11:08
需要 登录 后方可回复, 如果你还没有账号请点击这里 注册