web-dev-qa-db-ja.com

ActiveRecord-多態的な関連付けのクエリ

プロジェクトでコメントを追跡するために、多態的な関連付けを使用しています。すべて非常に単純なものです。

私が抱えている問題は、多態的な関連付けに基づいてクエリを実行し、Commentモデルからその所有者に結合することです。

そう ...

コメントモデルがあります

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

そして、ForumTopicsモード:

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

現在、重要ではない「コメント可能な」モデルが他にもいくつかあります。このすべてが動作します。

私がやろうとしていることは、指定された条件(この場合、 'featured' == true)を持つForumTopicに属するすべてのコメントを見つけることです。

Finderを使用してモデルに参加しようとすると:

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

次のエラーが表示されます。

ポリモーフィックアソシエーションを熱心にロードできません:コメント可能

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:不明なテーブル 'コメント':SELECTコメント。 FROMコメントforum_topics WHERE(forum_topics.featured = 1)*

(ここで、基になるクエリの構文が完全にオフになっており、結合がまったくないことがわかります)。

私がやっていることも可能かどうかわからず、必要な結果を達成する他の方法がありますが、それはそのようですすべき実行可能です。

何か案は?不足しているものはありますか?

43
Toby Hede

ああ!

問題を見つけたと思います。

経由で参加する場合:

@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

32
Toby Hede

古い質問ですが、ポリモーフィックと一緒に特定のタイプの直接関連付けを設定することにより、これを実現するためのより明確な方法があります。

#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_topics to includes厄介な結合の必要性を取り除く:

@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
28
Sam Peacey

条件付き、プラス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のコメントに感謝します。

19
Joshua Pinter

「コメント可能」を使用して関連付けを持つ別のモデルを導入すると、承認されたソリューションは機能しません。 commentable_idは一意ではないため、間違ったコメントの取得を開始します。

例えば:

コメントを受け入れるニュースモデルを追加することにしました...

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

クエリを使用して、idが1のforum_topicとIDが1のニュース記事にコメントを付けた場合、2つのレコードが返されることがあります。

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

おそらく、条件の1つとしてcommentable_typeを指定することで問題を解決できますが、それがこの問題に取り組む最善の方法ではないと思います。

17
Peter

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 を参照してください。

個人的には、未加工のSQL構文が少ないソリューション1を好みます。

11
prograils

私はこの投稿に出くわし、それが私の解決策につながりました。私の条件の1つとしてcommentable_typeを使用していますが、代わりにLEFT OUTER JOINを使用しています。これにより、コメントのないフォーラムトピックが含まれます。

LEFT OUTER JOIN `comments` ON `comments`.`commentable_id` = `forum_topics`.`id` AND `comments`.`commentable_type` = 'ForumTopic'
10
Chris Barnes

Railsにはデフォルトでポリモーフィック結合は含まれていませんが、このGemを使用すると、ポリモーフィック関係を簡単に結合できます。 https://github.com/jameshuynh/polymorphic_join

0
Damian Sia