Je pense que vos modèles ressemblent à ceci :
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
Vous ne pouvez pas faire cette requête pour plusieurs raisons.
- ActiveRecord est incapable de construire la jointure sans informations supplémentaires.
- Il n'y a pas de table appelée reviewable
Pour résoudre ce problème, vous devez définir explicitement la relation entre Review
y 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
Vous pouvez alors faire une requête comme ceci :
Review.includes(:shop).where(shops: {shop_type: 'cafe'})
Remarquez que le nom de la table est shops
et non reviewable
. Il ne devrait pas y avoir de table appelée reviewable dans la base de données.
Je pense que c'est plus facile et plus souple que de définir explicitement l'option de l'utilisateur. join
entre Review
y Shop
puisqu'il vous permet d'effectuer un chargement rapide en plus de l'interrogation par champs connexes.
La raison pour laquelle cela est nécessaire est qu'ActiveRecord ne peut pas construire une jointure basée uniquement sur reviewable, puisque plusieurs tables représentent l'autre extrémité de la jointure, et SQL, pour autant que je sache, ne permet pas de joindre une table nommée par la valeur stockée dans une colonne. En définissant la relation supplémentaire belongs_to :shop
vous donnez à ActiveRecord les informations dont il a besoin pour effectuer la jointure.