Dugaan saya adalah model Anda terlihat seperti ini:
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
Anda tidak dapat melakukan kueri itu karena beberapa alasan.
- ActiveRecord tidak dapat membuat gabungan tanpa informasi tambahan.
- Tidak ada tabel yang disebut dapat ditinjau
Untuk mengatasi masalah ini, Anda perlu secara eksplisit menentukan hubungan antara Review
dan 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
Kemudian Anda dapat melakukan kueri seperti ini:
Review.includes(:shop).where(shops: {shop_type: 'cafe'})
Perhatikan bahwa nama tabel adalah shops
dan bukan reviewable
. Seharusnya tidak ada tabel yang disebut dapat ditinjau dalam database.
Saya percaya ini akan lebih mudah dan lebih fleksibel daripada secara eksplisit mendefinisikan join
antara Review
dan Shop
karena memungkinkan Anda untuk memuat dengan bersemangat sebagai tambahan untuk membuat kueri berdasarkan bidang terkait.
Alasan mengapa hal ini diperlukan adalah karena ActiveRecord tidak dapat membuat gabungan berdasarkan dapat ditinjau saja, karena beberapa tabel mewakili ujung gabungan lainnya, dan SQL, sejauh yang saya tahu, tidak mengizinkan Anda bergabung dengan tabel yang diberi nama berdasarkan nilai yang disimpan di kolom. Dengan menentukan hubungan ekstra belongs_to :shop
, Anda memberikan informasi yang dibutuhkan ActiveRecord untuk menyelesaikan penggabungan.
@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)
:reviewable
ituShop
. Foto milik Toko.belongs_to :shop, foreign_type: 'Shop', foreign_key: 'reviewable_id'
reviews
termasuk eager memuat yang terkaitshop
menggunakan kodeReview.includes(:shop)
, definisi milik_to memunculkanbelongs_to :shop, -> { where(reviews: {reviewable_type: 'Shop'}) }, foreign_key: 'reviewable_id'
kesalahan yang mengatakanmissing FROM-clause entry for table "reviews"
. Saya memperbaikinya dengan memperbarui definisibelongs_to :shop, -> { joins(:reviews) .where(reviews: {reviewable_type: 'Shop'}) }, foreign_key: 'reviewable_id'
Jika Anda mendapatkan ActiveRecord :: EagerLoadPolymorphicError, itu karena
includes
diputuskan untuk memanggileager_load
ketika asosiasi polimorfik hanya didukung olehpreload
. Ada dalam dokumentasinya di sini: http://api.rubyonrails.org/v5.1/classes/ActiveRecord/EagerLoadPolymorphicError.htmlJadi selalu gunakan
preload
untuk asosiasi polimorfik. Ada satu peringatan untuk ini: Anda tidak dapat menanyakan asosiasi polimorfik di mana klausa (yang masuk akal, karena asosiasi polimorfik mewakili beberapa tabel.)sumber
Saat Anda menggunakan fragmen SQL dengan WHERE, referensi diperlukan untuk menggabungkan asosiasi Anda.
sumber
Sebagai tambahan, jawaban di atas, yang sangat bagus, Anda juga dapat menentukan
:include
pada pengaitan jika karena alasan tertentu kueri yang Anda gunakan tidak termasuk tabel model dan Anda mendapatkan kesalahan tabel yang tidak ditentukan.Seperti:
Tanpa
:include
opsi, jika Anda hanya mengakses pengaitanreview.shop
dalam contoh di atas, Anda akan mendapatkan kesalahan UndefinedTable (diuji di Rails 3, bukan 4) karena pengaitan akan dilakukanSELECT FROM shops WHERE shop.id = 1 AND ( reviews.review_type = 'Shop' )
.The
:include
pilihan akan memaksa JOIN bukan. :)sumber