119

使用 Rails 3.2,这段代码有什么问题?

@reviews = @user.reviews.includes(:user, :reviewable)
.where('reviewable_type = ? AND reviewable.shop_type = ?', 'Shop', 'cafe')

它引发了这个错误:

不能急切地加载多态关联:reviewable

如果我删除reviewable.shop_type = ?条件,它会起作用。

如何根据reviewable_typeand reviewable.shop_type(实际上是shop.shop_type)进行过滤?

4

6 回答 6

227

我的猜测是你的模型看起来像这样:

class User < ActiveRecord::Base
  has_many :reviews
end

class Review < ActiveRecord::Base
  belongs_to :user
  belongs_to :reviewable, polymorphic: true
end

class Shop < ActiveRecord::Base
  has_many :reviews, as: :reviewable
end

由于多种原因,您无法执行该查询。

  1. ActiveRecord 无法在没有其他信息的情况下构建连接。
  2. 没有称为可审查的表

Review要解决这个问题,您需要明确定义和之间的关系Shop

class Review < ActiveRecord::Base
   belongs_to :user
   belongs_to :reviewable, polymorphic: true
   # For Rails < 4
   belongs_to :shop, foreign_key: 'reviewable_id', conditions: "reviews.reviewable_type = 'Shop'"
   # For Rails >= 4
   belongs_to :shop, -> { where(reviews: {reviewable_type: 'Shop'}) }, foreign_key: 'reviewable_id'
   # Ensure review.shop returns nil unless review.reviewable_type == "Shop"
   def shop
     return unless reviewable_type == "Shop"
     super
   end
end

然后你可以这样查询:

Review.includes(:shop).where(shops: {shop_type: 'cafe'})

请注意,表名是shops而不是reviewable。数据库中不应该有一个名为可审查的表。

我相信这比显式定义join之间更容易和更灵活ReviewShop因为除了通过相关字段查询之外,它还允许您预先加载。

这样做是必要的原因是 ActiveRecord 无法单独基于可审查的方式构建连接,因为多个表代表连接的另一端,据我所知,SQL 不允许您连接由存储的值命名的表在一列中。通过定义额外的关系belongs_to :shop,您可以为 ActiveRecord 提供完成连接所需的信息。

于 2013-04-20T19:28:38.883 回答
22

如果你得到一个 ActiveRecord::EagerLoadPolymorphicError,那是因为includes决定eager_load在多态关联只支持时调用preload。它在这里的文档中:http: //api.rubyonrails.org/v5.1/classes/ActiveRecord/EagerLoadPolymorphicError.html

所以总是preload用于多态关联。对此有一个警告:您不能在 where 子句中查询多态关联(这是有道理的,因为多态关联代表多个表。)

于 2017-10-19T20:43:56.343 回答
1

这为我做了工作

  belongs_to :shop, foreign_type: 'Shop', foreign_key: 'reviewable_id'

于 2021-11-02T15:59:12.930 回答
1

没有足够的声誉来评论扩展上述摩西卢卡斯的回应,我不得不做一个小的调整来让它在 Rails 7 中工作,因为我收到以下错误:

ArgumentError: Unknown key: :foreign_type. Valid keys are: :class_name, :anonymous_class, :primary_key, :foreign_key, :dependent, :validate, :inverse_of, :strict_loading, :autosave, :required, :touch, :polymorphic, :counter_cache, :optional, :default

代替 belongs_to :shop, foreign_type: 'Shop', foreign_key: 'reviewable_id'

我和 belongs_to :shop, class_name: 'Shop', foreign_key: 'reviewable_id'

这里唯一的区别是更改foreign_type:class_name:!

于 2022-01-08T23:52:31.293 回答
0

作为附录,顶部的答案非常好,:include如果由于某种原因您使用的查询不包括模型的表并且您遇到未定义的表错误,您还可以指定关联。

像这样:

belongs_to :shop, 
           foreign_key: 'reviewable_id', 
           conditions: "reviews.reviewable_type = 'Shop'",
           include: :reviews

如果没有该:include选项,如果您仅访问review.shop上面示例中的关联,您将收到 UndefinedTable 错误(在 Rails 3 中测试,而不是 4 中测试),因为关联可以SELECT FROM shops WHERE shop.id = 1 AND ( reviews.review_type = 'Shop' )

:include选项将强制执行 JOIN。:)

于 2014-10-20T20:50:37.110 回答
-1
@reviews = @user.reviews.includes(:user, :reviewable)
.where('reviewable_type = ? AND reviewable.shop_type = ?', 'Shop', 'cafe').references(:reviewable)

当您在 WHERE 中使用 SQL 片段时,需要引用才能加入您的关联。

于 2015-08-02T13:20:41.023 回答