ActiveRecord - querying polymorphic associations

前端 未结 8 1345
深忆病人
深忆病人 2020-12-04 14:10

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 asso

相关标签:
8条回答
  • 2020-12-04 14:42

    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'
    
    0 讨论(0)
  • 2020-12-04 14:44

    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 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.

    0 讨论(0)
  • 2020-12-04 14:45

    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.

    0 讨论(0)
  • 2020-12-04 14:51

    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
    
    0 讨论(0)
  • 2020-12-04 14:51

    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.

    0 讨论(0)
  • 2020-12-04 14:55

    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

    0 讨论(0)
提交回复
热议问题