Bright load polymorphic

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

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

This error occurs:

You can't look forward to polymorphic associations: an overview

Should I remove the condition reviewable.shop_type = ? , it will work.

How to filter based on reviewable_type and reviewable.shop_type (actually shop.shop_type )?

+74
ruby-on-rails activerecord ruby-on-rails-3
Apr 20 '13 at 18:00
source share
4 answers

I assume 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 cannot complete this request for several reasons.

  • ActiveRecord cannot create a connection without additional information.
  • There is no table called overview

To solve this problem, you need to explicitly define the relationship between Review and 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 

Then you can request the following:

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

Note that the table name is shops , not reviewable . There should not be a table in the database that can be viewed in the database.

I find this easier and more flexible than directly defining a join between Review and Shop , as it allows you to get the load in addition to querying related fields.

The reason why this is necessary is because ActiveRecord cannot create a connection based on just considered ones, since several tables represent the other end of the connection, and SQL, as far as I know, does not allow you to join the table named value stored in column. belongs_to :shop defining an additional belongs_to :shop association, you provide ActiveRecord with the information necessary to complete the connection.

+150
Apr 20 '13 at 19:28
source share

As an addition, the answer from above, which is excellent, you can also specify :include in the association, if for any reason the query you are using does not include the model table and you get undefined table errors.

Same:

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

Without the :include parameter, if you just access the review.shop association in the above example, 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' ) .

The :include option will force JOIN instead. :)

+2
Oct 20 '14 at 20:50
source share
 @reviews = @user.reviews.includes(:user, :reviewable) .where('reviewable_type = ? AND reviewable.shop_type = ?', 'Shop', 'cafe').references(:reviewable) 

When you use SQL fragments with WHERE, references are needed to combine your association.

0
Aug 2 '15 at 13:20
source share

If you get ActiveRecord :: EagerLoadPolymorphicError, it is because includes decided to call eager_load when polymorphic associations are supported only by preload . This is in the documentation: http://api.rubyonrails.org/v5.1/classes/ActiveRecord/EagerLoadPolymorphicError.html

Therefore, always use preload for polymorphic associations. There is one caveat for this: you cannot query a polymorphic join in cases where clauses are present (which makes sense since the polymorphic association is a few tables.)

0
Oct 19 '17 at 20:43 on
source share



All Articles