एगर लोड पॉलिमॉर्फिक


104

रेल 3.2 का उपयोग करना, इस कोड में क्या गलत है?

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

यह इस त्रुटि को उठाता है:

पॉलिमॉर्फिक एसोसिएशन को उत्सुकता से लोड नहीं किया जा सकता है: समीक्षा योग्य

अगर मैं reviewable.shop_type = ?शर्त हटाता हूं , तो यह काम करता है।

मैं पर कैसे आधार पर फ़िल्टर कर सकते हैं reviewable_typeऔर reviewable.shop_type(जो वास्तव में है shop.shop_type)?

जवाबों:


207

मेरा अनुमान है कि आपके मॉडल इस तरह दिखते हैं:

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. समीक्षात्मक नामक कोई तालिका नहीं है

इस समस्या को हल करने के लिए, आपको स्पष्ट रूप से 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 को उस जानकारी को दे रहे हैं जो जुड़ने को पूरा करने के लिए आवश्यक है।


6
वास्तव में मैंने कुछ और घोषित किए बिना इसका उपयोग किया:@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)
विक्टर

1
ऐसा इसलिए :reviewableहै Shop। तस्वीरें शॉप की हैं।
विक्टर

6
rails4 में काम किया है, लेकिन एक डेप्रिसिएशन चेतावनी देगा, इसमें कहा गया है कि has_many: spam_comments, -> {जहाँ स्पैम: true}, class_name: 'टिप्पणी' जैसी शैली का उपयोग करना चाहिए। तो rails4 में, be_to: shop, -> {कहाँ ("समीक्षाएँ.reviewable_type = 'Shop'")}, विदेशी_की: 'reviewable_id' होगा। लेकिन ध्यान रखें, Review.includes (: shop) त्रुटि उठाएगा, यह अवश्य होगा जहां एक खंड में पट्टे पर अपील।
रेकिन

49
belongs_to :shop, foreign_type: 'Shop', foreign_key: 'reviewable_id'
विदेशी_टाइप

14
जब कोड reviewsलोड हो रहा है का shopउपयोग करते हुए संबंधित लोडिंग सहित उत्सुक लोड हो रहा है, तो Review.includes(:shop) परिभाषा belongs_to :shop, -> { where(reviews: {reviewable_type: 'Shop'}) }, foreign_key: 'reviewable_id' यह कहकर त्रुटि को फेंक देती है missing FROM-clause entry for table "reviews"। मैंने इसे निम्नलिखित तरीके से rel_to परिभाषा को अपडेट करके तय किया: belongs_to :shop, -> { joins(:reviews) .where(reviews: {reviewable_type: 'Shop'}) }, foreign_key: 'reviewable_id'
जिग्नेश गोहेल

12

यदि आपको एक ActiveRecord :: EagerLoadPolymorphicError मिलता है, तो इसका कारण यह है कि includesकॉल करने का निर्णय eager_loadतब लिया जाता है जब पॉलीमॉर्फिक एसोसिएशन केवल समर्थित होते हैं preload। यह यहाँ प्रलेखन में है: http://api.rubyonrails.org/v5.1/classes/ActiveRecord/EagerLoadPolymorphicError.html

इसलिए हमेशा preloadबहुरूपी संघों के लिए उपयोग करें । इसके लिए एक चेतावनी है: आप पॉलीमॉर्फिक अटैक को क्वेरी नहीं कर सकते हैं जहां क्लॉज़ (जो समझ में आता है, क्योंकि पॉलिमॉर्फिक एसोसिएशन कई तालिकाओं का प्रतिनिधित्व करता है।)


मुझे लगता है कि यह एक तरीका है जो मार्गदर्शक में प्रलेखित नहीं है: guide.rubyonrails.org/…
MSC

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

जब आप SQL अंशों को WHERE के साथ उपयोग कर रहे हैं, तो आपके एसोसिएशन में शामिल होने के लिए संदर्भ आवश्यक है।


0

एक परिशिष्ट के रूप में शीर्ष पर उत्तर, जो उत्कृष्ट है, आप :includeएसोसिएशन पर भी निर्दिष्ट कर सकते हैं यदि किसी कारण से आप जिस क्वेरी का उपयोग कर रहे हैं वह मॉडल की तालिका में शामिल नहीं है और आपको अपरिभाषित तालिका त्रुटियां मिल रही हैं।

इस तरह:

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

:includeविकल्प के बिना , यदि आप केवल review.shopऊपर दिए गए उदाहरण में एसोसिएशन का उपयोग करते हैं, तो आपको एक अपरिभाषित त्रुटि मिलेगी (रेल में परीक्षण 3, 4 नहीं) क्योंकि एसोसिएशन करेगी SELECT FROM shops WHERE shop.id = 1 AND ( reviews.review_type = 'Shop' )

:includeविकल्प के लिए एक के बजाय शामिल हों बाध्य करेगा। :)


5
अज्ञात कुंजी:: स्थितियां। मान्य कुंजी हैं: वर्ग_नाम,: वर्ग, विदेशी_की, वैध:: ऑटोसेव, आश्रित: प्राथमिक_की,: उलटा_फ,: आवश्यक,: विदेशी_प्रकार: बहुरूपता, स्पर्श:
प्रतिवाद
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.