ActiveRecord - interrogazione associazioni polimorfiche
-
22-08-2019 - |
Domanda
Sto usando associazioni polimorfiche per monitorare Commenti nel mio progetto. Tutta roba in avanti molto semplice.
Il problema che ho è nella interrogazione sulla base della associazione polimorfica e unendo dal modello Commento di nuovo al suo proprietario.
...
Ho un modello Commento
class Comment < ActiveRecord::Base
belongs_to :commentable, :polymorphic => true
end
E una modalità ForumTopics:
class ForumTopic < ActiveRecord::Base
has_many :comments, :as => :commentable
end
Ho diversi altri modelli "commentabile" che non sono importanti in questo momento. Tutto questo funziona.
Quello che sto cercando di fare è trovare tutti i commenti che appartengono ad un ForumTopic con una condizione specificata (in questo caso, 'optional' == true).
Quando provo e uso un cercatore di unire i modelli:
@comments = Comment.find(:all
:joins => :commentable
:conditions => ["forum_topics.featured = ? ", true]
)
ricevo il seguente errore:
Non può caricare con entusiasmo l'associazione polimorfica: commentabile
Uso della AR "includono sintassi":
@comments = Comment.find(:all
:include => :forum_topics
:conditions => ["forum_topics.featured = ? ", true]
)
restituisce:
non è stato trovato Associazione denominata 'forum_topics'; forse scritto male vero?
Se provo e unisco con un nome di tabella invece del nome dell'associazione (stringa invece del simbolo):
@comments = Comment.find(:all,
:joins => "forum_topics",
:conditions => ["forum_topics.featured = ? ", true]
)
Vedo:
Mysql :: Errore: 'commenti' tavolo sconosciuta:. Selezionate commenti dai commenti forum_topics WHERE (forum_topics.featured = 1) *
(Si può vedere qui che la sintassi della query sottostante è completamente spento e il join manca del tutto).
Non sono sicuro se quello che sto facendo è ancora possibile, e ci sono altri modi per ottenere il risultato richiesto, ma sembra che dovrebbe essere fattibile.
Tutte le idee? Tutto ciò che mi manca?
Soluzione
Argh!
Credo di aver trovato il problema.
Quando si uniscono tramite:
@comments = Comment.find(:all,
:joins => "forum_topics",
:conditions => ["forum_topics.featured = ? ", true]
)
È necessario l'intero unirsi!
:joins => "INNER JOIN forum_topics ON forum_topics.id = comments.commentable_id",
Si veda la sempre impressionante: http://guides.rubyonrails.org/active_record_querying.html#joining-tables
Altri suggerimenti
Una vecchia questione, ma c'è un modo più pulito di ottenere tale risultato con la creazione di un'associazione diretta per il tipo specifico insieme al polimorfico:
#comment.rb
class Comment < ActiveRecord::Base
belongs_to :commentable, polymorphic: true
belongs_to :forum_topics, -> { where( comments: { commentable_type: 'ForumTopic' } ).includes( :comments ) }, foreign_key: 'commentable_id'
...
end
Si sono quindi in grado di passare a :forum_topics
includes
sbarazzarsi della necessità di un disordinato join:
@comments = Comment
.includes( :forum_topics )
.where( :forum_topics => { featured: true } )
Si potrebbe quindi pulire ulteriormente questo in su spostando la query in un campo di applicazione:
#comment.rb
class Comment < ActiveRecord::Base
...
scope :featured_topics, -> {
includes( :forum_topics )
.where( :forum_topics => { featured: true } )
}
...
end
lasciando di essere in grado di fare semplicemente
@comments = Comment.featured_topics
Hai bisogno di una condizionale, più il Rails 3 +
Un sacco di gente accennato nelle risposte e commenti, ma ho sentito che le persone, me compreso, otterrebbe sgambetto se ho atterrato qui e non hanno letto abbastanza attentamente.
Quindi, ecco la risposta corretta, tra cui il condizionale che è assolutamente necessario.
@comments = Comment.joins( "INNER JOIN forum_topics ON comments.commentable_id = forum_topics.id" )
.where( comments: { commentable_type: 'ForumTopic' } )
.where( forum_topics: { featured: true } )
Grazie a tutti, specialmente @Jits, @Peter e @prograils per i loro commenti.
La soluzione accettata non funziona una volta che si introduce un altro modello che ha un'associazione con "commentabile". commentable_id non è unico e quindi potrai iniziare il recupero dei commenti sbagliati.
Ad esempio:
Si decide di aggiungere un modello di notizie che accetta commenti ...
class News < ActiveRecord::Base
has_many :comments, :as => :commentable
end
Ora si può ottenere due record indietro se hai fatto un commento su un forum_topic con un ID di 1 e un articolo di notizie con un ID di 1 utilizzando query:
:joins => "INNER JOIN forum_topics ON forum_topics.id = comments.commentable_id"
Si potrebbe forse risolvere il problema fornendo una commentable_type come una delle vostre condizioni, ma non credo che sia il modo migliore per affrontare questo problema.
Mi sono imbattuto in questo post e mi portano a mia soluzione. Utilizzando la commentable_type come una delle mie condizioni, ma utilizzando un join esterno sinistro, invece. saranno inclusi che gli argomenti del forum modo senza commenti.
LEFT OUTER JOIN `comments` ON `comments`.`commentable_id` = `forum_topics`.`id` AND `comments`.`commentable_type` = 'ForumTopic'
Controllato a lavorare sotto Rails 5 :
Soluzione 1:
@comments = Comment
.where(commentable_type: "ForumTopic")
.joins("INNER JOIN forum_topics ON comments.commentable_id = forum_topics.id")
.where(forum_topics: {featured: true})
.all
o
Soluzione 2:
@comments = Comment
.joins("INNER JOIN forum_topics ON comments.commentable_id = forum_topics.id AND comments.commentable_type = 'ForumTopic'")
.where(forum_topics: {featured: true}).all
Attenzione alla sintassi SQL grezzo: nessun backticks sono ammessi. Vedere http://guides.rubyonrails.org/active_record_querying.html#joining-tables.
Io personalmente preferisco Soluzione 1 in quanto contiene meno sintassi SQL crudo.
Rails non include un polimorfica unirsi per default, ma questo gioiello aiuterebbe a si unisce alla tua relazione polimorfica con facilità. https://github.com/jameshuynh/polymorphic_join