2013-04-20 9 views
78

Rails 3.2を使用すると、このコードで何が問題になりますか?Eager load polymorphic

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

それは、このエラーが発生します。

私はreviewable.shop_type = ?条件を削除すると、それが動作します。

reviewable_typereviewable.shop_type(実際にはshop.shop_type)に基づいてどのようにフィルタリングできますか?

答えて

161

私の推測では、あなたのモデルは次のように見えることである:

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. 何の表がありませんが、この問題を解決するために検討可能

と呼ばれ、明示的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 

次に、あなたは次のように問い合わせることができます:テーブル名がshopsないreviewableある

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

ていることに注意してください。データベースにreviewableというテーブルがあってはいけません。

ReviewShopの間に明示的にjoinを定義するより簡単で柔軟性があると思います。これは、関連するフィールドでのクエリに加えて熱心に負荷をかけることができるためです。

これが必要な理由は、複数のテーブルが結合のもう一方の端を表しているため、ActiveRecordがレビュー可能なだけで結合を構築できないことです.SQLは、わかっている限り、列に格納されている値で除算します。余分な関係belongs_to :shopを定義することで、ActiveRecordは結合を完了するために必要な情報を提供します。

+3

完璧な答えと詳細な説明。ありがとう。 – Victor

+4

実際に私は何も宣言することなくこれを使い終えました: '@reviews = @ user.reviews.joins(" INNER JOINショップON(reviewable_type = 'ショップ' AND shops.id = reviewable_id AND stores.shop_type = '"+タイプ+ ":") ")。includes(:user、:reviewable =>:photos)' – Victor

+0

これは機能しますか? –

1

補足としては、何かの理由で使用しているクエリがモデルのテーブルを含んでおらず、未定義のテーブルエラーが発生している場合は、:includeをアソシエーションに指定することもできます。あなたは、単に上記の例では関連review.shopにアクセスする場合は関連がするので、あなたは(Railsの3、いない4でテスト)UndefinedTableエラーが発生します:includeオプションを指定しない

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

、:そう同様

do SELECT FROM shops WHERE shop.id = 1 AND (reviews.review_type = 'Shop')

:includeオプションは強制的にJOINを実行します。:)

+2

不明なキー::条件。有効なキーは::class_name、:class、:foreign_key、:validate、:autosave、:従属、:primary_key、:inverse_of、:必須、:foreign_type、:多型、:touch、:counter_cache – Bengala

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

WHEREでSQLフラグメントを使用している場合は、関連付けに参加するための参照が必要です。

0

あなたはActiveRecordの:: EagerLoadPolymorphicErrorを取得する場合includesは、ポリモーフィック団体のみpreloadによってサポートされているときeager_loadを呼び出すことにしましたので、それはです。 http://api.rubyonrails.org/v5.1/classes/ActiveRecord/EagerLoadPolymorphicError.html

多形性の関連付けには常にpreloadを使用してください。これには1つの注意点があります。多項式の関連は複数の表を表すので、where句の中の多相結合を照会することはできません。

関連する問題