ActiveRecord - associations polymorphes interroger
-
22-08-2019 - |
Question
J'utilise des associations polymorphiques pour suivre les commentaires de mon projet. Toutes choses très direct.
Le problème que j'ai est basé sur l'interrogation de l'association polymorphes et se joindre à partir du modèle de commentaire Retour à son propriétaire.
...
J'ai un modèle de commentaire
class Comment < ActiveRecord::Base
belongs_to :commentable, :polymorphic => true
end
Et un mode ForumTopics:
class ForumTopic < ActiveRecord::Base
has_many :comments, :as => :commentable
end
J'ai plusieurs autres modèles « commentable » qui ne sont pas importants en ce moment. Tout cela fonctionne.
Ce que je suis en train de faire est de trouver tous les commentaires qui appartiennent à un ForumTopic avec une condition spécifiée (dans ce cas, « en vedette » == true).
Quand j'essaie d'utiliser un viseur pour rejoindre les modèles:
@comments = Comment.find(:all
:joins => :commentable
:conditions => ["forum_topics.featured = ? ", true]
)
Je reçois l'erreur suivante:
ne peut pas charger avec impatience l'association polymorphique: commentable
Utilisation de l'AR "comprend la syntaxe":
@comments = Comment.find(:all
:include => :forum_topics
:conditions => ["forum_topics.featured = ? ", true]
)
renvoie:
Association nommée 'forum_topics' n'a pas été trouvé; peut-être vous avez mal orthographié il?
Si je tente et se joindre à un nom de table au lieu du nom d'association (chaîne au lieu de symbole):
@comments = Comment.find(:all,
:joins => "forum_topics",
:conditions => ["forum_topics.featured = ? ", true]
)
Je vois:
Mysql :: Erreur: 'Commentaires' une table inconnue:. SELECT commentaires des commentaires forum_topics OU (forum_topics.featured = 1) *
(Vous pouvez voir ici que la syntaxe de la requête sous-jacente est totalement hors tension et la jointure manque tout à fait).
Je ne sais pas si ce que je fais est même possible, et il y a d'autres façons d'atteindre le résultat recherché, mais il semble que ce devrait faisable.
Toutes les idées? Tout ce que je suis absent?
La solution
Argh!
Je pense avoir trouvé le problème.
Lors de l'assemblage via:
@comments = Comment.find(:all,
:joins => "forum_topics",
:conditions => ["forum_topics.featured = ? ", true]
)
Vous avez besoin du tout rejoindre!
:joins => "INNER JOIN forum_topics ON forum_topics.id = comments.commentable_id",
Voir la toujours impressionnante: http://guides.rubyonrails.org/active_record_querying.html#joining-tables
Autres conseils
Une vieille question, mais il y a un moyen plus propre d'y parvenir en créant une association directe pour le type spécifique avec le polymorphes:
#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
Vous êtes alors en mesure de passer à :forum_topics
includes
se débarrasser de la nécessité d'un désordre rejoindre:
@comments = Comment
.includes( :forum_topics )
.where( :forum_topics => { featured: true } )
Vous pouvez ensuite encore nettoyer ce en déplaçant la requête dans un champ:
#comment.rb
class Comment < ActiveRecord::Base
...
scope :featured_topics, -> {
includes( :forum_topics )
.where( :forum_topics => { featured: true } )
}
...
end
Laissant de pouvoir faire simplement
@comments = Comment.featured_topics
Vous avez besoin d'un conditionnel, Grandes rails 3 +
Beaucoup de gens fait allusion dans les réponses et les commentaires, mais je sentais que les gens, y compris moi-même, obtiendraient trébuché si je suis arrivé ici et ne pas lire assez bien.
Alors, voici la bonne réponse, y compris le conditionnel qui est absolument nécessaire.
@comments = Comment.joins( "INNER JOIN forum_topics ON comments.commentable_id = forum_topics.id" )
.where( comments: { commentable_type: 'ForumTopic' } )
.where( forum_topics: { featured: true } )
Merci à tous, en particulier @Jits, @Peter et @prograils pour leurs commentaires.
La solution retenue ne fonctionne pas une fois que vous introduisez un autre modèle qui a une association avec « commentable ». commentable_id n'est pas unique et donc vous allez commencer à récupérer les mauvais commentaires.
Par exemple:
Vous décidez d'ajouter un modèle de nouvelles qui accepte les commentaires ...
class News < ActiveRecord::Base
has_many :comments, :as => :commentable
end
Maintenant, vous pouvez obtenir deux enregistrements en arrière si vous avez fait un commentaire sur forum_topic avec un identifiant de 1 et un article de nouvelles avec un identifiant de 1 en utilisant votre requête:
:joins => "INNER JOIN forum_topics ON forum_topics.id = comments.commentable_id"
Vous pourriez probablement résoudre le problème en fournissant un commentable_type comme l'un de vos conditions, mais je ne pense pas que ce soit la meilleure façon d'aborder cette question.
Je suis tombé sur ce poste et il me mène à ma solution. Utilisation de la commentable_type comme l'un de mes conditions mais en utilisant un LEFT OUTER JOIN à la place. Que les sujets du forum façon sans commentaires seront inclus.
LEFT OUTER JOIN `comments` ON `comments`.`commentable_id` = `forum_topics`.`id` AND `comments`.`commentable_type` = 'ForumTopic'
cochés à travailler sous Rails 5 :
Solution 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
ou
Solution 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
Faites attention à la syntaxe SQL brute: pas de contre-apostrophes sont autorisés. Voir http://guides.rubyonrails.org/active_record_querying.html#joining-tables.
Personnellement, je préfère Solution 1 car il contient moins de syntaxe SQL brute.
Rails ne comprend pas polymorphes rejoindre par défaut, mais ce petit bijou vous aiderait à relier votre relation polymorphes avec facilité. https://github.com/jameshuynh/polymorphic_join