Я предполагаю, что ваши модели выглядят так:
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
Вы не можете выполнить этот запрос по нескольким причинам.
- ActiveRecord не может создать соединение без дополнительной информации.
- Нет таблицы с названием для проверки
Чтобы решить эту проблему, вам необходимо явно определить связь между 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
между Review
и, Shop
поскольку это позволяет вам выполнять загрузку в дополнение к запросам по связанным полям.
Причина, по которой это необходимо, заключается в том, что ActiveRecord не может построить соединение на основе только проверяемого, поскольку несколько таблиц представляют другой конец соединения, а SQL, насколько мне известно, не позволяет вам присоединиться к таблице, названной сохраненным значением в столбце. Определяя дополнительную связь belongs_to :shop
, вы даете ActiveRecord информацию, необходимую для завершения соединения.
@reviews = @user.reviews.joins("INNER JOIN shops ON (reviewable_type = 'Shop' AND shops.id = reviewable_id AND shops.shop_type = '" + type + "')").includes(:user, :reviewable => :photos)
:reviewable
естьShop
. Фотографии принадлежат Магазин.belongs_to :shop, foreign_type: 'Shop', foreign_key: 'reviewable_id'
reviews
включая нетерпеливую загрузку связанногоshop
с использованием кода,Review.includes(:shop)
определение own_tobelongs_to :shop, -> { where(reviews: {reviewable_type: 'Shop'}) }, foreign_key: 'reviewable_id'
выдает сообщение об ошибкеmissing FROM-clause entry for table "reviews"
. Я исправил это, обновив определение own_to следующим образом:belongs_to :shop, -> { joins(:reviews) .where(reviews: {reviewable_type: 'Shop'}) }, foreign_key: 'reviewable_id'
Если вы получили ActiveRecord :: EagerLoadPolymorphicError, это связано с тем, что было
includes
решено вызывать,eager_load
когда полиморфные ассоциации поддерживаются толькоpreload
. Это в документации здесь: http://api.rubyonrails.org/v5.1/classes/ActiveRecord/EagerLoadPolymorphicError.htmlПоэтому всегда используйте
preload
для полиморфных ассоциаций. Здесь есть одно предостережение: вы не можете запрашивать полиморфную ассоциацию в предложениях where (что имеет смысл, поскольку полиморфная ассоциация представляет несколько таблиц).источник
Когда вы используете фрагменты SQL с WHERE, ссылки необходимы для присоединения к вашей ассоциации.
источник
В качестве дополнения ответ вверху, который превосходен, вы также можете указать
:include
связь, если по какой-то причине используемый вами запрос не включает таблицу модели, и вы получаете неопределенные ошибки таблицы.Вот так:
Без этой
:include
опции, если вы просто обращаетесь к ассоциацииreview.shop
в приведенном выше примере, вы получите ошибку UndefinedTable (проверено в Rails 3, а не 4), потому что ассоциация будет работатьSELECT FROM shops WHERE shop.id = 1 AND ( reviews.review_type = 'Shop' )
.:include
Опция заставит JOIN вместо этого. :)источник