1. ホーム
  2. ruby-on-rails

[解決済み] イーガー・ロード・ポリモーフィック

2022-08-23 06:38:40

質問

Rails 3.2を使っていますが、このコードのどこが問題なのでしょうか?

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

このようなエラーが発生します。

多相アソシエーション:reviewableをeagerlyにロードすることができません。

を削除すると reviewable.shop_type = ? の条件を削除すると、動作します。

どのようにすれば reviewable_typereviewable.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. reviewable という名前のテーブルはありません。

この問題を解決するためには、明示的に ReviewShop .

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 の間に ReviewShop を使うことで、関連するフィールドによるクエリに加えて、イーガー・ロードが可能になるからです。

これが必要な理由は、ActiveRecordではレビュー可能なものだけに基づいて結合を構築できないからです。なぜなら、複数のテーブルが結合の相手側を表すからです。 余分なリレーションシップを定義することで belongs_to :shop を定義することで、ActiveRecordに結合を完了するために必要な情報を与えていることになります。