ActiveRecord - 查询多态关联

Tob*_*ede 39 ruby activerecord ruby-on-rails polymorphic-associations

我正在使用多态关联来跟踪我的项目中的注释.所有非常直接的东西.

我遇到的问题是基于多态关联查询并从Comment模型加入到它的所有者.

所以......

我有一个评论模型

class Comment < ActiveRecord::Base
  belongs_to :commentable, :polymorphic => true
end
Run Code Online (Sandbox Code Playgroud)

和ForumTopics模式:

class ForumTopic < ActiveRecord::Base
  has_many :comments, :as => :commentable
end
Run Code Online (Sandbox Code Playgroud)

我还有其他几个"可评论"的模型,现在并不重要.所有这一切都有效.

我想要做的是找到属于具有指定条件的ForumTopic的所有注释(在这种情况下,'featured'== true).

当我尝试使用finder加入模型时:

@comments = Comment.find(:all 
            :joins => :commentable
            :conditions => ["forum_topics.featured = ? ", true] 
            )
Run Code Online (Sandbox Code Playgroud)

我收到以下错误:

无法急切加载多态关联:可评论

使用AR"包含语法":

@comments = Comment.find(:all 
            :include => :forum_topics
            :conditions => ["forum_topics.featured = ? ", true] 
            )
Run Code Online (Sandbox Code Playgroud)

收益:

未找到名为"forum_topics"的协会; 也许你拼错了吗?

如果我尝试使用表名而不是关联名(字符串而不是符号)加入:

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

我知道了:

Mysql ::错误:未知表'comments':SELECT注释.FROM comments forum_topics WHERE(forum_topics.featured = 1)*

(您可以在此处看到底层查询的语法完全关闭,并且连接完全丢失).

不确定我正在做什么甚至是可能的,并且还有其他方法来实现所需的结果,但似乎它应该是可行的.

有任何想法吗?我错过了什么?

Tob*_*ede 31

哎呀!

我想我发现了这个问题.

加入时:

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

你需要整个加入!

:joins => "INNER JOIN forum_topics ON forum_topics.id = comments.commentable_id",
Run Code Online (Sandbox Code Playgroud)

看到非常棒的:http: //guides.rubyonrails.org/active_record_querying.html#joining-tables

  • 我认为你还需要检查`comments.commentable_type`是正确的类型(在你的情况下是`"ForumTopic"`)否则你正在与所有匹配`forum_topics.id`的`comments.commentable_id`条目进行连接,即使他们不是论坛主题. (27认同)
  • 当错误如此令人难以置信的简单时,你不恨它吗? (4认同)
  • 查看下面 Chris barnes 的回答,以澄清 Jit 的评论。基本上你会添加一个 'AND comments.commentable_type = "ForumTopic"' (2认同)

Sam*_*cey 27

一个老问题,但有一种更简洁的方法来实现这一点,通过建立特定类型的直接关联以及多态:

#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
Run Code Online (Sandbox Code Playgroud)

然后,您可以通过:forum_topicsincludes摆脱需要一个凌乱的连接:

@comments = Comment
  .includes( :forum_topics )
  .where( :forum_topics => { featured: true } )
Run Code Online (Sandbox Code Playgroud)

然后,您可以通过将查询移动到范围中来进一步清理它:

#comment.rb
class Comment < ActiveRecord::Base

  ...

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

  ...

end
Run Code Online (Sandbox Code Playgroud)

让你能够干脆做到

@comments = Comment.featured_topics
Run Code Online (Sandbox Code Playgroud)

  • @ArupRakshit,您是否已尝试将您的特定关联声明为:审核而不是:评论?我认为"belongs_to"应该始终与单一的关系名称一起使用. (3认同)

Jos*_*ter 18

你需要一个有条件的,加Rails 3+

许多人在答案和评论中提到了它,但我觉得,如果我在这里登陆并且阅读不够彻底,包括我在内的人会被绊倒.

所以,这是正确的答案,包括绝对必要的条件.

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

感谢所有人,特别是@Jits,@ Peter和@prograils的评论.

  • @WashingtonBotelho 这很重要,因为`Comment` 是多态的,而`INNER JOIN` 只是比较`Comment` 模型的`id`。因此,如果`Comment` 属于多个模型,例如`ForumPost`,那么该`INNER JOIN` 将错误地包含也属于`ForumPost` 的`Comment` 记录。 (2认同)

小智 16

一旦引入另一个使用"可评论"关联的模型,接受的解决方案就不起作用.commentable_id不是唯一的,因此您将开始检索错误的注释.

例如:

您决定添加接受评论的新闻模型......

class News < ActiveRecord::Base
   has_many :comments, :as => :commentable
end
Run Code Online (Sandbox Code Playgroud)

现在,如果您对id为1的forum_topic和使用您的查询的ID为1的新闻文章发表评论,您可能会收到两条记录:

:joins => "INNER JOIN forum_topics ON forum_topics.id = comments.commentable_id"
Run Code Online (Sandbox Code Playgroud)

您可以通过提供commentable_type作为您的条件之一来解决问题,但我认为这不是解决此问题的最佳方法.

  • 实际上我认为你建议提供commentable_type作为其中一个条件是正确的方法...... [`act_as_taggable_on`](https://github.com/mbleigh/acts-as-taggable-on)插件完成它们的`taggings`表包含(其中包括)列:`tag_id`,`taggable_id`**和`taggable_type`** (2认同)

小智 10

我遇到了这篇文章,它引导我找到我的解决方案.使用commentable_type作为我的条件之一,但使用LEFT OUTER JOIN代替.这样,将包含没有评论的论坛主题.

LEFT OUTER JOIN `comments` ON `comments`.`commentable_id` = `forum_topics`.`id` AND `comments`.`commentable_type` = 'ForumTopic'
Run Code Online (Sandbox Code Playgroud)


pro*_*ils 9

检查在Rails 5下工作:

解决方案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
Run Code Online (Sandbox Code Playgroud)

要么

解决方案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
Run Code Online (Sandbox Code Playgroud)

注意原始SQL语法:不允许反引号.请参阅http://guides.rubyonrails.org/active_record_querying.html#joining-tables.

我个人更喜欢解决方案1,因为它包含较少的原始SQL语法.