使用Rails 3.2,这段代码出了什么问题?
@reviews = @user.reviews.includes(:user, :reviewable)
.where('reviewable_type = ? AND reviewable.shop_type = ?', 'Shop', 'cafe')
它引发了这个错误:
无法急切加载多态关联:可审核
如果我删除了reviewable.shop_type = ?
条件,则无效。
如何根据reviewable_type
和reviewable.shop_type
(实际为shop.shop_type
)进行过滤?
答案 0 :(得分:186)
我的猜测是你的模型看起来像这样:
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
由于多种原因,您无法执行该查询。
要解决此问题,您需要明确定义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
。数据库中不应该有一个名为reviewable的表。
我认为这比明确定义join
和Review
之间的Shop
更容易,也更灵活,因为除了通过相关字段查询外,它还允许您急切加载。
这是必要的原因是ActiveRecord不能单独基于可审阅的方式构建连接,因为多个表代表连接的另一端,而且据我所知,SQL不允许你加入一个名为的表。存储在列中的值。通过定义额外关系belongs_to :shop
,您将为ActiveRecord提供完成连接所需的信息。
答案 1 :(得分:4)
如果你得到一个ActiveRecord :: EagerLoadPolymorphicError,那是因为当includes
仅支持多态关联时,eager_load
决定调用preload
。它在这里的文档中:http://api.rubyonrails.org/v5.1/classes/ActiveRecord/EagerLoadPolymorphicError.html
所以总是使用preload
进行多态关联。有一点需要注意:你不能在where子句中查询多态关联(这是有意义的,因为多态关联代表多个表。)
答案 2 :(得分:0)
作为附录,顶部的答案非常好,如果由于某种原因您使用的查询不包括模型表而且您是关于某种原因,您还可以在关联上指定:include
得到未定义的表错误。
像这样:
belongs_to :shop,
foreign_key: 'reviewable_id',
conditions: "reviews.reviewable_type = 'Shop'",
include: :reviews
如果没有:include
选项,如果您只是访问上面示例中的关联review.shop
,您将收到UndefinedTable错误(在Rails 3中测试,而不是4),因为关联将执行{{ 1}}。
SELECT FROM shops WHERE shop.id = 1 AND ( reviews.review_type = 'Shop' )
选项会强制使用JOIN。 :)
答案 3 :(得分:0)
@reviews = @user.reviews.includes(:user, :reviewable)
.where('reviewable_type = ? AND reviewable.shop_type = ?', 'Shop', 'cafe').references(:reviewable)
当您将SQL片段与WHERE一起使用时,需要引用以加入您的关联。