ActiveRecord - querying polymorphic associations

大兔子大兔子 提交于 2019-11-27 18:08:39

Argh!

I think I found the problem.

When joining via:

@comments = Comment.find(:all,
        :joins => "forum_topics",
        :conditions => ["forum_topics.featured = ? ", true] 
        )

You need the whole join!

:joins => "INNER JOIN forum_topics ON forum_topics.id = comments.commentable_id",

See the ever-awesome: http://guides.rubyonrails.org/active_record_querying.html#joining-tables

An old question, but there is a cleaner way of achieving this by setting up a direct association for the specific type along with the polymorphic:

#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

You are then able to pass :forum_topics to includes getting rid of the need for a messy join:

@comments = Comment
  .includes( :forum_topics )
  .where( :forum_topics => { featured: true } )

You could then further clean this up by moving the query into a scope:

#comment.rb
class Comment < ActiveRecord::Base

  ...

  scope :featured_topics, -> { 
    includes( :forum_topics )
    .where( :forum_topics => { featured: true } ) 
  }

  ...

end

Leaving you to be able to simply do

@comments = Comment.featured_topics

You Need a Conditional, Plus Rails 3+

A lot of people alluded to it in the answers and comments but I felt that people, including myself, would get tripped up if I landed here and didn't read thoroughly enough.

So, here's the proper answer, including the conditional that is absolutely necessary.

@comments = Comment.joins( "INNER JOIN forum_topics ON comments.commentable_id = forum_topics.id" )
                   .where( comments:     { commentable_type: 'ForumTopic' } )
                   .where( forum_topics: { featured:         true         } )

Thanks to all, especially @Jits, @Peter, and @prograils for their comments.

The accepted solution does not work once you introduce another model that has an association using "commentable". commentable_id is not unique and therefore you'll start retrieving the wrong comments.

For example:

You decide to add a news model that accepts comments...

class News < ActiveRecord::Base
   has_many :comments, :as => :commentable
end

Now you may get two records back if you made a comment on a forum_topic with an id of 1 and a news article with an id of 1 using your query:

:joins => "INNER JOIN forum_topics ON forum_topics.id = comments.commentable_id"

You could probably solve the problem by supplying a commentable_type as one of your conditions, but I don't think that's the best way to approach this issue.

I came across this post and it lead me to my solution. Using the commentable_type as one of my conditions but using a LEFT OUTER JOIN instead. That way forum topics without comments will be included.

LEFT OUTER JOIN `comments` ON `comments`.`commentable_id` = `forum_topics`.`id` AND `comments`.`commentable_type` = 'ForumTopic'
prograils

Checked to work under 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

or

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

Pay attention to the raw SQL syntax: no backticks are allowed. See http://guides.rubyonrails.org/active_record_querying.html#joining-tables .

I personally prefer Solution 1 as it contains fewer raw SQL syntax.

Rails does not include a polymorphic join by default but this gem would help you to joins your polymorphic relationship with ease. https://github.com/jameshuynh/polymorphic_join

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!