ActiveRecord - consultando associações polimórficas
-
22-08-2019 - |
Pergunta
Eu estou usando associações polimórficas para acompanhar comentários no meu projeto. Todas as coisas muito para a frente.
O problema que tenho é em consulta com base na associação polimórfica e juntando-se a partir do Comentário modelo de volta a ele é proprietário.
Então ...
Eu tenho um modelo Comment
class Comment < ActiveRecord::Base
belongs_to :commentable, :polymorphic => true
end
E um modo ForumTopics:
class ForumTopic < ActiveRecord::Base
has_many :comments, :as => :commentable
end
Eu tenho vários outros modelos "commentable" que não são importantes agora. Tudo isso funciona.
O que estou tentando fazer é encontrar todos os Comentários que pertencem a um ForumTopic com uma condição especificada (neste caso, 'destaque' == true).
Quando tento usar um localizador para juntar os modelos:
@comments = Comment.find(:all
:joins => :commentable
:conditions => ["forum_topics.featured = ? ", true]
)
Eu recebo o seguinte erro:
não pode ansiosamente carregar a associação polimórfica: commentable
Usando a AR "incluem sintaxe":
@comments = Comment.find(:all
:include => :forum_topics
:conditions => ["forum_topics.featured = ? ", true]
)
retornos:
associação denominada 'forum_topics' não foi encontrado; talvez você grafada-lo?
Se eu tentar e juntar-se com um nome de tabela em vez do nome de associação (string em vez de símbolo):
@comments = Comment.find(:all,
:joins => "forum_topics",
:conditions => ["forum_topics.featured = ? ", true]
)
Eu vejo:
Mysql :: Erro: tabela desconhecido 'comentários':. Observações Select dos comentários forum_topics WHERE (forum_topics.featured = 1) *
(Você pode ver aqui que a sintaxe da consulta subjacente é totalmente fora e a junção está faltando completamente).
Não tenho certeza se o que eu estou fazendo é mesmo possível, e há outras maneiras de conseguir o resultado necessário, mas parece que ele deve ser factível.
Todas as idéias? Qualquer coisa que eu estou ausente?
Solução
Argh!
Eu acho que encontrei o problema.
Ao aderir via:
@comments = Comment.find(:all,
:joins => "forum_topics",
:conditions => ["forum_topics.featured = ? ", true]
)
Você precisa se juntar ao todo!
:joins => "INNER JOIN forum_topics ON forum_topics.id = comments.commentable_id",
Veja a sempre impressionante: http://guides.rubyonrails.org/active_record_querying.html#joining-tables
Outras dicas
Uma questão de idade, mas há uma maneira mais limpa de conseguir isso através da criação de uma associação direta para o tipo específico juntamente com o polimórfica:
#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
Está, então, capaz de passar :forum_topics
para includes
livrar-se da necessidade de um confuso juntar-se:
@comments = Comment
.includes( :forum_topics )
.where( :forum_topics => { featured: true } )
Você poderia, então, limpar ainda mais esta se movendo a consulta em um escopo:
#comment.rb
class Comment < ActiveRecord::Base
...
scope :featured_topics, -> {
includes( :forum_topics )
.where( :forum_topics => { featured: true } )
}
...
end
Deixando-o de ser capaz de simplesmente fazer
@comments = Comment.featured_topics
Você precisa de um condicional, Além disso, o Rails 3 +
Um monte de pessoas aludidas-lo nas respostas e comentários, mas eu senti que as pessoas, inclusive eu, iria tropeçar se eu desembarcou aqui e não ler cuidadosamente o suficiente.
Então, aqui está a resposta adequada, incluindo a condicional que é absolutamente necessário.
@comments = Comment.joins( "INNER JOIN forum_topics ON comments.commentable_id = forum_topics.id" )
.where( comments: { commentable_type: 'ForumTopic' } )
.where( forum_topics: { featured: true } )
Obrigado a todos, especialmente @Jits, @ Pedro, e @prograils para seus comentários.
A solução aceita não funciona uma vez que você introduzir um outro modelo que tem uma associação usando "commentable". commentable_id não é única e, portanto, você vai começar a recuperar os comentários erradas.
Por exemplo:
Você decide adicionar um modelo notícia de que aceita comentários ...
class News < ActiveRecord::Base
has_many :comments, :as => :commentable
end
Agora você pode obter dois registros de volta se você fez um comentário sobre um forum_topic com um ID de 1 e uma notícia com um id 1 usando sua consulta:
:joins => "INNER JOIN forum_topics ON forum_topics.id = comments.commentable_id"
Você provavelmente poderia resolver o problema fornecendo um commentable_type como uma das suas condições, mas eu não acho que é a melhor maneira de abordar esta questão.
me deparei com este post e me levar a minha solução. Usando o commentable_type como uma das minhas condições, mas usando um LEFT OUTER JOIN em vez. Que tópicos do fórum maneira sem comentários serão incluídos.
LEFT OUTER JOIN `comments` ON `comments`.`commentable_id` = `forum_topics`.`id` AND `comments`.`commentable_type` = 'ForumTopic'
verificado ao trabalho sob Trilhos 5 :
Solução 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
Solução 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
Preste atenção à sintaxe SQL RAW: No backticks são permitidos. Consulte http://guides.rubyonrails.org/active_record_querying.html#joining-tables.
Eu pessoalmente prefiro Solução 1, uma vez que contém menos sintaxe SQL cru.
Rails não inclui um polimórfico se juntar por padrão, mas esta jóia iria ajudá-lo a se junta o seu relacionamento polimórfico com facilidade. https://github.com/jameshuynh/polymorphic_join