ActiveRecord - consulta de las asociaciones polimórficas
-
22-08-2019 - |
Pregunta
Estoy utilizando asociaciones polimórficas para rastrear Comentarios en mi proyecto. Todo esto es muy muy directa.
El problema que tengo es en la consulta sobre la base de la asociación polimórfica y uniéndose a partir del modelo de comentario de nuevo a su propietario.
Así que ...
Tengo un modelo de comentario
class Comment < ActiveRecord::Base
belongs_to :commentable, :polymorphic => true
end
Y un modo de ForumTopics:
class ForumTopic < ActiveRecord::Base
has_many :comments, :as => :commentable
end
Tengo varios otros modelos "commentable" que no son importantes en este momento. Todo esto funciona.
Lo que estoy tratando de hacer es encontrar todos los Comentarios que pertenecen a un ForumTopic con una condición especificada (en este caso, 'ofrecido' == true).
Cuando intento y utilizar un buscador para unirse a los modelos:
@comments = Comment.find(:all
:joins => :commentable
:conditions => ["forum_topics.featured = ? ", true]
)
recibo el siguiente error:
puede no cargar con entusiasmo la asociación polimórfica: commentable
Uso de la AR "incluyen sintaxis":
@comments = Comment.find(:all
:include => :forum_topics
:conditions => ["forum_topics.featured = ? ", true]
)
retornos:
No se encontró asociación con nombre '' forum_topics; tal vez mal escrito que?
Si lo intento y me uno con un nombre de tabla en lugar del nombre de la asociación (cadena en lugar de símbolo):
@comments = Comment.find(:all,
:joins => "forum_topics",
:conditions => ["forum_topics.featured = ? ", true]
)
veo:
MySQL :: error: 'comentarios' Desconocido tabla:. SELECT comentarios DESDE DONDE comentarios forum_topics (forum_topics.featured = 1) *
(Se puede ver aquí que la sintaxis de la consulta subyacente es totalmente apagado y la unión no se encuentra en total).
No estoy seguro si lo que estoy haciendo es incluso posible, y hay otras maneras de lograr el resultado deseado, pero parece que debe sea factible.
¿Alguna idea? Cualquier cosa que me falta?
Solución
Argh!
Creo que he encontrado el problema.
Cuando se unen a través de:
@comments = Comment.find(:all,
:joins => "forum_topics",
:conditions => ["forum_topics.featured = ? ", true]
)
Es necesario el conjunto unirse!
:joins => "INNER JOIN forum_topics ON forum_topics.id = comments.commentable_id",
Vea la siempre impresionante: http://guides.rubyonrails.org/active_record_querying.html#joining-tables
Otros consejos
Una cuestión de edad, pero hay una forma más limpia de lograr esto mediante la creación de una asociación directa para el tipo específico junto con el polimórfico:
#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
Que luego son capaces de pasar a :forum_topics
includes
deshacerse de la necesidad de un desordenado unirse a:
@comments = Comment
.includes( :forum_topics )
.where( :forum_topics => { featured: true } )
A continuación, podría limpiar aún más este movimiento por la consulta en un ámbito de aplicación:
#comment.rb
class Comment < ActiveRecord::Base
...
scope :featured_topics, -> {
includes( :forum_topics )
.where( :forum_topics => { featured: true } )
}
...
end
Dejando que sea capaz de hacer simplemente
@comments = Comment.featured_topics
¿Necesita un condicional, rieles Plus 3 +
Una gran cantidad de personas que se alude a ella en las respuestas y comentarios, pero sentí que la gente, incluido yo mismo, conseguirían tropezar si aterricé aquí y no haber leído a fondo suficiente.
Por lo tanto, aquí está la respuesta correcta, incluyendo el condicional es decir absolutamente es necesario.
@comments = Comment.joins( "INNER JOIN forum_topics ON comments.commentable_id = forum_topics.id" )
.where( comments: { commentable_type: 'ForumTopic' } )
.where( forum_topics: { featured: true } )
Gracias a todos, especialmente @Jits, @Peter y @prograils por sus comentarios.
La solución aceptada no funciona una vez que se introduce otro modelo que tiene una asociación usando "commentable". commentable_id no es única y por lo tanto se va a empezar a recuperar los comentarios equivocados.
Por ejemplo:
Se decide agregar un modelo de prensa que acepta los comentarios ...
class News < ActiveRecord::Base
has_many :comments, :as => :commentable
end
Ahora es posible obtener dos registros de regreso si ha realizado un comentario en un forum_topic con un ID de 1 y un artículo de noticias con un id de 1 utilizando su consulta:
:joins => "INNER JOIN forum_topics ON forum_topics.id = comments.commentable_id"
Probablemente se podría resolver el problema mediante el suministro de un commentable_type como uno de sus condiciones, pero no creo que esa es la mejor manera de abordar este problema.
Me encontré con este post y me llevo a mi solución. Utilizando el commentable_type como una de mis condiciones, pero utilizando una combinación externa izquierda en su lugar. Se incluirán los temas en los que camino sin comentarios.
LEFT OUTER JOIN `comments` ON `comments`.`commentable_id` = `forum_topics`.`id` AND `comments`.`commentable_type` = 'ForumTopic'
A cuadros para trabajar bajo Rails 5 :
Solución 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
Solución 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
Prestar atención a la sintaxis SQL prima: no hay acentos abiertos están permitidos. Ver http://guides.rubyonrails.org/active_record_querying.html#joining-tables.
Yo personalmente prefiero la solución 1, ya que contiene menos de sintaxis SQL prima.
Carriles no incluye un polimorfo se unen por defecto, pero esta joya le ayudaría a se une a su relación polimórfica con facilidad. https://github.com/jameshuynh/polymorphic_join