51

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

我遇到的问题是基于多态关联进行查询并从评论模型加入到它的所有者。

所以 ...

我有一个评论模型

class Comment < ActiveRecord::Base
  belongs_to :commentable, :polymorphic => true
end

还有一个 ForumTopics 模式:

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

我还有其他几个现在不重要的“可评论”模型。所有这些都有效。

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

当我尝试使用查找器加入模型时:

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

我收到以下错误:

不能急切地加载多态关联:commentable

使用 AR“包含语法”:

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

返回:

未找到名为“forum_topics”的关联;也许你拼错了?

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

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

我懂了:

Mysql::Error: Unknown table 'comments': SELECT comments。来自评论 forum_topics WHERE (forum_topics.featured = 1)*

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

不确定我正在做的事情是否可行,还有其他方法可以达到所需的结果,但似乎应该是可行的。

有任何想法吗?有什么我想念的吗?

4

8 回答 8

36

啊!

我想我找到了问题所在。

通过以下方式加入时:

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

你需要整个加入!

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

看到令人敬畏的:http: //guides.rubyonrails.org/active_record_querying.html#joining-tables

于 2009-03-25T03:59:40.343 回答
34

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

#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

然后,您可以摆脱对混乱连接的需要:forum_topicsincludes

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

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

#comment.rb
class Comment < ActiveRecord::Base

  ...

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

  ...

end

让你能够简单地做

@comments = Comment.featured_topics
于 2015-08-10T07:31:02.040 回答
23

你需要一个有条件的 Plus 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         } )

感谢所有人,尤其是 @Jits、@Peter 和 @prograils 的评论。

于 2014-04-10T17:38:03.770 回答
20

检查在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

或者

解决方案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

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

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

于 2017-07-20T13:26:31.000 回答
17

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

例如:

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

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

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

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

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

于 2009-08-11T14:32:47.520 回答
10

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

LEFT OUTER JOIN `comments` ON `comments`.`commentable_id` = `forum_topics`.`id` AND `comments`.`commentable_type` = 'ForumTopic'
于 2012-02-23T19:07:45.383 回答
2

这是一个适应您的目的的示例。

class AdwordsCampaign < ApplicationRecord
  has_many :daily_campaign_reports, as: :ga_campaign
end

class DailyCampaignReport < ApplicationRecord

  belongs_to :ga_campaign, polymorphic: true

  # scope
  def self.joins_ga_campaign(model)
    models = model.to_s.pluralize
    sql = <<~SQL
      INNER JOIN #{models}
      ON #{models}.id = daily_campaign_reports.ga_campaign_id
    SQL
    joins(sql).where(ga_campaign_type: model.to_s.camelize)
  end
end

然后我可以在任何需要的地方使用它,如下所示:

DailyCampaignReport.joins_ga_campaign(:adwords_campaign)
于 2020-09-23T08:37:12.943 回答
0

默认情况下,Rails 不包含多态连接,但这个 gem 可以帮助您轻松地连接多态关系。https://github.com/jameshuynh/polymorphic_join

于 2019-04-10T07:35:28.137 回答