Rails 3.2を使用すると、このコードの何が問題になっていますか?
@reviews = @user.reviews.includes(:user, :reviewable)
.where('reviewable_type = ? AND reviewable.shop_type = ?', 'Shop', 'cafe')
このエラーが発生します:
ポリモーフィックな関連付けを熱心にロードできません:reviewable
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
いくつかの理由により、そのクエリを実行できません。
この問題を解決するには、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
であることに注意してください。データベースにreviewableと呼ばれるテーブルがあってはなりません。
join
とReview
の間でShop
を明示的に定義するよりも簡単で柔軟だと思います。関連フィールドによるクエリに加えて負荷を積極的にかけることができるからです。
これが必要な理由は、複数のテーブルが結合のもう一方の端を表すため、ActiveRecordはレビュー可能な単独に基づいて結合を構築できないためです。SQLは、私が知る限り、列に。追加の関係を定義するbelongs_to :shop
、ActiveRecordに参加を完了するために必要な情報を提供しています。
ActiveRecord :: EagerLoadPolymorphicErrorが発生した場合、includes
がeager_load
ポリモーフィックな関連付けがpreload
でのみサポートされている場合。次のドキュメントにあります: http://api.rubyonrails.org/v5.1/classes/ActiveRecord/EagerLoadPolymorphicError.html
したがって、多態的な関連付けには常にpreload
を使用してください。これには1つの注意点があります。where句でポリモーフィックアソシエーションを照会することはできません(ポリモーフィックな関連付けは複数のテーブルを表すため、これは理にかなっています)。
@reviews = @user.reviews.includes(:user, :reviewable)
.where('reviewable_type = ? AND reviewable.shop_type = ?', 'Shop', 'cafe').references(:reviewable)
WHEREでSQLフラグメントを使用している場合、関連付けを結合するには参照が必要です。
優れた補遺の答えとして、何らかの理由で使用しているクエリにモデルのテーブルが含まれておらず、未定義のテーブルエラーが発生している場合は、関連付けに_:include
_を指定することもできます。
そのようです:
_belongs_to :shop,
foreign_key: 'reviewable_id',
conditions: "reviews.reviewable_type = 'Shop'",
include: :reviews
_
_:include
_オプションを使用しない場合、上記の例で関連付け_review.shop
_にアクセスするだけの場合、UndefinedTableエラーが発生します(Rails 3ではなく4でテスト)関連付けはSELECT FROM shops WHERE shop.id = 1 AND ( reviews.review_type = 'Shop' )
を実行します。
_:include
_オプションは、代わりに強制的にJOINを実行します。 :)