Яркая нагрузка полиморфная

Используя 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 )?

    Я предполагаю, что ваши модели выглядят так:

     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 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 

    Затем вы можете запросить следующее:

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

    Обратите внимание, что имя таблицы - это shops и не reviewable . Не должно быть таблицы, которую можно просмотреть в базе данных.

    Я считаю, что это проще и гибче, чем прямое определение join между Review and Shop поскольку оно позволяет вам получать нагрузку в дополнение к запросам по связанным полям.

    Причина, по которой это необходимо, заключается в том, что ActiveRecord не может создавать соединение на основе только рассмотренных, поскольку несколько таблиц представляют собой другой конец соединения, а SQL, насколько мне известно, не позволяет вам присоединиться к таблице, названной сохраненным значением в столбце. Определив дополнительную взаимосвязь belongs_to :shop , вы предоставляете ActiveRecord информацию, необходимую для завершения объединения.

    Если вы получаете ActiveRecord :: EagerLoadPolymorphicError, это потому, что includes решение вызвать eager_load когда полиморфные ассоциации поддерживаются только preload eager_load . Он находится в документации: http://api.rubyonrails.org/v5.1/classes/ActiveRecord/EagerLoadPolymorphicError.html

    Поэтому всегда используйте preload для полиморфных ассоциаций. Для этого существует одна оговорка: вы не можете запрашивать полиморфное объединение в тех случаях, когда клаузулы (что имеет смысл, поскольку полиморфная ассоциация представляет собой несколько таблиц).

    В качестве добавления ответ наверху, который является превосходным, вы также можете указать :include в ассоциацию, если по какой-либо причине используемый вами запрос не включает таблицу модели, и вы получаете неопределенные ошибки таблицы.

    Вот так:

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

    Без опции :include если вы просто review.shop доступ к review.shop связи review.shop в приведенном выше примере, вы получите ошибку UndefinedTable (протестированную в Rails 3, а не 4), потому что ассоциация будет делать SELECT FROM shops WHERE shop.id = 1 AND ( reviews.review_type = 'Shop' ) .

    Опция :include будет заставлять JOIN вместо этого. 🙂

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

    Когда вы используете SQL-fragmentы с WHERE, ссылки необходимы для присоединения к вашей ассоциации.

    Давайте будем гением компьютера.