Ich vermute, dass Ihre Modelle so aussehen:
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
Sie können diese Abfrage aus mehreren Gründen nicht ausführen.
- ActiveRecord kann den Join ohne zusätzliche Informationen nicht erstellen.
- Es gibt keine Tabelle mit der Bezeichnung "überprüfbar"
Um dieses Problem zu lösen, müssen Sie die Beziehung zwischen Review
und explizit definieren 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
Dann können Sie wie folgt abfragen:
Review.includes(:shop).where(shops: {shop_type: 'cafe'})
Beachten Sie, dass der Tabellenname lautet shops
und nicht reviewable
. In der Datenbank sollte keine Tabelle mit dem Namen überprüfbar vorhanden sein.
Ich glaube, dies ist einfacher und flexibler als das explizite Definieren des join
Dazwischen Review
und Shop
da es Ihnen ermöglicht, zusätzlich zum Abfragen nach verwandten Feldern eifrig zu laden.
Der Grund dafür ist, dass ActiveRecord keinen Join erstellen kann, der nur auf überprüfbar basiert, da mehrere Tabellen das andere Ende des Joins darstellen und SQL meines Wissens nicht zulässt, dass Sie einer Tabelle beitreten, die nach dem gespeicherten Wert benannt ist in einer Spalte. Indem Sie die zusätzliche Beziehung definieren belongs_to :shop
, geben Sie ActiveRecord die Informationen, die zum Abschließen des Joins erforderlich sind.
@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
liegt daranShop
. Fotos gehören zum Shop.belongs_to :shop, foreign_type: 'Shop', foreign_key: 'reviewable_id'
reviews
einschließlich des eifrigen Ladens des zugehörigenshop
Codes, wird inReview.includes(:shop)
der Definition davon_zu diebelongs_to :shop, -> { where(reviews: {reviewable_type: 'Shop'}) }, foreign_key: 'reviewable_id'
Fehlermeldung ausgegebenmissing FROM-clause entry for table "reviews"
. Ich habe es behoben, indem ich Gehorsame_Definition auf folgende Weise aktualisiert habe:belongs_to :shop, -> { joins(:reviews) .where(reviews: {reviewable_type: 'Shop'}) }, foreign_key: 'reviewable_id'
Wenn Sie einen ActiveRecord :: EagerLoadPolymorphicError erhalten, liegt dies daran, dass Sie
includes
sich für einen Aufruf entscheiden,eager_load
wenn polymorphe Assoziationen nur von unterstützt werdenpreload
. Es befindet sich in der Dokumentation hier: http://api.rubyonrails.org/v5.1/classes/ActiveRecord/EagerLoadPolymorphicError.htmlAlso immer
preload
für polymorphe Assoziationen verwenden. Hierfür gibt es eine Einschränkung: Sie können die polymorphe Zuordnung in where-Klauseln nicht abfragen (was sinnvoll ist, da die polymorphe Zuordnung mehrere Tabellen darstellt.)quelle
Wenn Sie SQL-Fragmente mit WHERE verwenden, sind Referenzen erforderlich, um Ihrer Zuordnung beizutreten.
quelle
Als Ergänzung zur Antwort oben, die ausgezeichnet ist, können Sie auch
:include
in der Zuordnung angeben , ob die von Ihnen verwendete Abfrage aus irgendeinem Grund die Modelltabelle nicht enthält und Sie undefinierte Tabellenfehler erhalten.Wie so:
:include
Wenn Sie ohne die Optionreview.shop
im obigen Beispiel lediglich auf die Zuordnung zugreifen , wird ein UndefinedTable-Fehler (getestet in Rails 3, nicht 4) angezeigt, da die Zuordnung dies tutSELECT FROM shops WHERE shop.id = 1 AND ( reviews.review_type = 'Shop' )
.Die
:include
Option erzwingt stattdessen einen JOIN. :) :)quelle