Ruby-on-rails 急切加载多态

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/16123492/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-02 21:50:38  来源:igfitidea点击:

Eager load polymorphic

ruby-on-railsruby-on-rails-3activerecord

提问by Victor

Using Rails 3.2, what's wrong with this code?

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

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

It raises this error:

它引发了这个错误:

Can not eagerly load the polymorphic association :reviewable

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

If I remove the reviewable.shop_type = ?condition, it works.

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

How can I filter based on the reviewable_typeand reviewable.shop_type(which is actually shop.shop_type)?

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

回答by Sean Hill

My guess is that your models look like this:

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

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

You are unable to do that query for several reasons.

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

  1. ActiveRecord is unable to build the join without additional information.
  2. There is no table called reviewable
  1. ActiveRecord 无法在没有附加信息的情况下构建连接。
  2. 没有名为reviewable的表

To solve this issue, you need to explicitly define the relationship between Reviewand Shop.

为了解决这个问题,你需要明确定义之间的关系ReviewShop

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

Then you can query like this:

然后你可以这样查询:

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

Notice that the table name is shopsand not reviewable. There should not be a table called reviewable in the database.

请注意,表名是shops而不是reviewable。数据库中不应有名为 reviewable 的表。

I believe this to be easier and more flexible than explicitly defining the joinbetween Reviewand Shopsince it allows you to eager load in addition to querying by related fields.

我相信这比明确定义join之间更容易和更灵活ReviewShop因为除了按相关字段进行查询之外,它还允许您急切加载。

The reason that this is necessary is that ActiveRecord cannot build a join based on reviewable alone, since multiple tables represent the other end of the join, and SQL, as far as I know, does not allow you join a table named by the value stored in a column. By defining the extra relationship belongs_to :shop, you are giving ActiveRecord the information it needs to complete the join.

之所以需要这样做,是因为 ActiveRecord 不能单独基于 reviewable 来构建连接,因为多个表代表了连接的另一端,而 SQL,据我所知,不允许您连接以存储的值命名的表在一列中。通过定义额外的关系belongs_to :shop,您将向 ActiveRecord 提供完成连接所需的信息。

回答by seanmorton

If you get an ActiveRecord::EagerLoadPolymorphicError, it's because includesdecided to call eager_loadwhen polymorphic associations are only supported by preload. It's in the documentation here: http://api.rubyonrails.org/v5.1/classes/ActiveRecord/EagerLoadPolymorphicError.html

如果你得到一个 ActiveRecord::EagerLoadPolymorphicError,那是因为includes决定eager_load在多态关联仅由preload. 它在此处的文档中:http: //api.rubyonrails.org/v5.1/classes/ActiveRecord/EagerLoadPolymorphicError.html

So always use preloadfor polymorphic associations. There is one caveat for this: you cannot query the polymorphic assocition in where clauses (which makes sense, since the polymorphic association represents multiple tables.)

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

回答by Stewart Mckinney

As an addendum the answer at the top, which is excellent, you can also specify :includeon the association if for some reason the query you are using is not including the model's table and you are getting undefined table errors.

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

Like so:

像这样:

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

Without the :includeoption, if you merely access the association review.shopin the example above, you will get an UndefinedTable error ( tested in Rails 3, not 4 ) because the association will do SELECT FROM shops WHERE shop.id = 1 AND ( reviews.review_type = 'Shop' ).

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

The :includeoption will force a JOIN instead. :)

:include选项将改为强制 JOIN。:)

回答by un_gars_la_cour

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

When you are using SQL fragments with WHERE, references is necessary to join your association.

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