I am using polymorphic associations to track Comments in my project. All very straight forward stuff.
The problem I have is in querying based on the polymorphic association and joining from the Comment model back to it's owner.
So ...
I have a Comment model
class Comment < ActiveRecord::Base
  belongs_to :commentable, :polymorphic => true
end
And a ForumTopics mode:
class ForumTopic < ActiveRecord::Base
  has_many :comments, :as => :commentable
end
I have several other "commentable" models that aren't important right now. All of this works.
What I am trying to do is find all of the Comments that belong to a ForumTopic with a specified condition (in this case, 'featured' == true).
When I try and use a finder to join the models:
@comments = Comment.find(:all 
            :joins => :commentable
            :conditions => ["forum_topics.featured = ? ", true] 
            )
I receive the following error:
Can not eagerly load the polymorphic association :commentable
Using the AR "include syntax":
@comments = Comment.find(:all 
            :include => :forum_topics
            :conditions => ["forum_topics.featured = ? ", true] 
            )
returns:
Association named 'forum_topics' was not found; perhaps you misspelled it?
If I try and join with a table name instead of the association name (string instead of symbol):
@comments = Comment.find(:all,
            :joins => "forum_topics",
            :conditions => ["forum_topics.featured = ? ", true] 
            )
I see:
Mysql::Error: Unknown table 'comments': SELECT comments. FROM comments forum_topics WHERE (forum_topics.featured = 1 )*
(You can see here that the syntax of the underlying query is totally off and the join is missing altogether).
Not sure if what I am doing is even possible, and there are other ways to achieve the required result but it seems like it should be doable.
Any ideas? Anything I am missing?
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
                        A lot of people alluded to it in the answers and comments but I felt that people, including myself, would get tripped up if they 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.
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With