İstekli yük polimorfik


104

Rails 3.2 kullanımı, bu kodda sorun nedir?

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

Bu hatayı ortaya çıkarır:

Polimorfik ilişki hevesle yüklenemiyor: incelenebilir

reviewable.shop_type = ?Koşulu kaldırırsam işe yarıyor.

reviewable_typeVe reviewable.shop_type(hangisi gerçekte shop.shop_type) temelinde nasıl filtreleyebilirim ?

Yanıtlar:


207

Tahminim, modellerin şöyle görünüyor:

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

Bu sorguyu birkaç nedenden dolayı yapamazsınız.

  1. ActiveRecord, ek bilgi olmadan birleştirme oluşturamaz.
  2. İncelenebilir diye bir tablo yok

Bu sorunu çözmek için, Reviewve arasındaki ilişkiyi açıkça tanımlamanız gerekir 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

Sonra şu şekilde sorgulayabilirsiniz:

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

Tablo adının olduğuna shopsve olmadığına dikkat edin reviewable. Veritabanında incelenebilir diye bir tablo olmamalıdır.

Bunu daha kolay ve açıkça tanımlamak yerine daha esnek olduğuna inandığımız joinarasında Reviewve Shopbunun ilgili alanlara göre sorgulama ek olarak istekli yüke olanak sağlar çünkü.

Bunun gerekli olmasının nedeni, ActiveRecord'un tek başına gözden geçirilebilirliğe dayalı bir birleştirme oluşturamamasıdır, çünkü birden çok tablo birleştirmenin diğer ucunu temsil eder ve bildiğim kadarıyla SQL, depolanan değere göre adlandırılan bir tabloya katılmanıza izin vermez. bir sütunda. Ekstra ilişkiyi tanımlayarak, belongs_to :shopActiveRecord'a birleştirme işlemini tamamlamak için ihtiyaç duyduğu bilgileri veriyorsunuz.


6
Aslında bunu daha fazla bir şey beyan etmeden kullandım:@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)
Victor

1
Çünkü :reviewableöyle Shop. Fotoğraflar Mağazaya aittir.
Victor

6
rails4'te çalıştı, ancak bir kullanımdan kaldırma uyarısı verecek, has_many: spam_comments, -> {spam: true}, class_name: 'Comment' gibi bir stil kullanması gerektiğini söyledi. Dolayısıyla, rails4'te, ait_to: dükkan, -> {nerede ("reviews.reviewable_type = 'Shop'")}, foreign_key: 'reviewable_id' olacaktır. Ancak dikkatli olun, Review.includes (: shop) hata verecektir, bu olmalıdır kira bir yerde fıkra ekleyin.
raykin

49
Benim için benzer bir sorun için çalışan foreign_type da var:belongs_to :shop, foreign_type: 'Shop', foreign_key: 'reviewable_id'
A5308Y

14
reviewsİlişkilendirilmiş shopkodu kullanarak istekli yükleme dahil olmak üzere yükleme sırasında Review.includes(:shop) , Own_to tanımı belongs_to :shop, -> { where(reviews: {reviewable_type: 'Shop'}) }, foreign_key: 'reviewable_id' hatası verir missing FROM-clause entry for table "reviews". Own_to tanımını aşağıdaki şekilde güncelleyerek düzelttim: belongs_to :shop, -> { joins(:reviews) .where(reviews: {reviewable_type: 'Shop'}) }, foreign_key: 'reviewable_id'
Jignesh Gohel

12

ActiveRecord :: EagerLoadPolymorphicError alırsanız, bunun nedeni includes aramaya karar eager_loadpolimorfik dernekler sadece tarafından desteklendiğinde preload. Buradaki belgelerde mevcuttur: http://api.rubyonrails.org/v5.1/classes/ActiveRecord/EagerLoadPolymorphicError.html

Bu yüzden her zaman preloadpolimorfik çağrışımlar için kullanın . Bunun için bir uyarı var: where cümlelerinde polimorfik ilişkilendirmeyi sorgulayamazsınız (bu mantıklıdır, çünkü polimorfik ilişki birden çok tabloyu temsil eder.)


Görüyorum ki bu, kılavuzlarda belgelenmeyen tek yöntem: guides.rubyonrails.org/…
MSC

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

WHERE ile SQL parçalarını kullandığınızda, ilişkilendirmenize katılmak için referanslar gereklidir.


0

Ek olarak, en üstteki cevabı mükemmel bir şekilde belirtebilirsiniz. :include , kullandığınız sorgu modelin tablosunu içermiyorsa ve tanımsız tablo hataları alıyorsanız ilişkilendirmede .

Şöyle:

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

:includeSeçenek olmadan, yalnızca review.shopyukarıdaki örnekteki ilişkiye erişirseniz, bir UndefinedTable hatası alırsınız (4 değil, Rails 3'te test edilmiştir) çünkü ilişkilendirme işe yarar SELECT FROM shops WHERE shop.id = 1 AND ( reviews.review_type = 'Shop' ).

:includeSeçenek yerine JOIN zorlayacaktır. :)


5
Bilinmeyen anahtar:: koşullar. Geçerli anahtarlar şunlardır:: class_name,: class,: foreign_key,: validate,: otomatik kaydetme,: bağımlı,: primary_key,: inverse_of,: gerekli,: foreign_type,: polymorphic,: touch,: counter_cache
Bengala,
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.