59

has_many :throughRailsで相互に通過する複数の関係を持つことは可能ですか?私が投稿した別の質問の解決策としてそうするようにという提案を受けましたが、それを機能させることができませんでした。

フレンドは、結合テーブルを介した循環的な関連付けです。目標はhas_many :throughforを作成することですfriends_comments。これにより、aを取得して、1回のクエリで彼の友人からのすべてのコメントを取得するUserようなことができます。user.friends_comments

class User
  has_many :friendships
  has_many :friends, 
           :through => :friendships,
           :conditions => "status = #{Friendship::FULL}"
  has_many :comments
  has_many :friends_comments, :through => :friends, :source => :comments
end

class Friendship < ActiveRecord::Base
  belongs_to :user
  belongs_to :friend, :class_name => "User", :foreign_key => "friend_id"
end

これは見栄えがよく、理にかなっていますが、私にはうまくいきません。これは、ユーザーのfriends_commentsにアクセスしようとしたときに関連する部分で発生するエラーです。
ERROR: column users.user_id does not exist
: SELECT "comments".* FROM "comments" INNER JOIN "users" ON "comments".user_id = "users".id WHERE (("users".user_id = 1) AND ((status = 2)))

user.friendsと入力するだけで、これが機能します。これは、実行されるクエリです。
: SELECT "users".* FROM "users" INNER JOIN "friendships" ON "users".id = "friendships".friend_id WHERE (("friendships".user_id = 1) AND ((status = 2)))

has_manyそのため、友情関係を通じてオリジナルを完全に忘れてしまい、Userクラスを結合テーブルとして不適切に使用しようとしているようです。

私は何か間違ったことをしていますか、それともこれは単に不可能ですか?

4

4 に答える 4

79

編集:

Rails 3.1は、ネストされた関連付けをサポートします。例えば:

has_many :tasks
has_many :assigments, :through => :tasks
has_many :users, :through => :assignments

以下の解決策は必要ありません。詳細については、このスクリーンキャストを参照してください。

元の回答

has_many :through別のアソシエーションのソースとしてアソシエーションを渡しますhas_many :through 。私はそれがうまくいくとは思わない。

  has_many :friends, 
           :through => :friendships,
           :conditions => "status = #{Friendship::FULL}"
  has_many :friends_comments, :through => :friends, :source => :comments

この問題を解決するには、3つのアプローチがあります。

1)関連付け拡張機能を作成します

 has_many  :friends, 
           :through => :friendships,
           :conditions => "status = #{Friendship::FULL}" do
     def comments(reload=false)
       @comments = nil if reload 
       @comments ||=Comment.find_all_by_user_id(map(&:id))
     end
 end

これで、友達のコメントを次のように取得できます。

user.friends.comments

User2)クラスにメソッドを追加します。

  def friends_comments(reload=false)
    @friends_comments = nil if reload 
    @friends_comments ||=Comment.find_all_by_user_id(self.friend_ids)
  end

これで、友達のコメントを次のように取得できます。

user.friends_comments

3)これをさらに効率的にしたい場合は、次のようにします。

  def friends_comments(reload=false)
    @friends_comments = nil if reload 
    @friends_comments ||=Comment.all( 
             :joins => "JOIN (SELECT friend_id AS user_id 
                              FROM   friendships 
                              WHERE  user_id = #{self.id}
                        ) AS friends ON comments.user_id = friends.user_id")
  end

これで、友達のコメントを次のように取得できます。

user.friends_comments

すべてのメソッドが結果をキャッシュします。結果をリロードする場合は、次のようにします。

user.friends_comments(true)
user.friends.comments(true)

またはさらに良い:

user.friends_comments(:reload)
user.friends.comments(:reload)
于 2010-03-04T23:44:53.717 に答える
8

あなたの問題を解決するプラグインがあります、このブログを見てください。

プラグインをインストールします

script/plugin install git://github.com/ianwhite/nested_has_many_through.git
于 2010-03-24T08:30:54.400 に答える
5

これは以前は機能しませんでしたが、Rails3.1では正常に機能するようになりました。

于 2011-12-01T19:17:48.077 に答える
3

このブログエントリは便利だと思いました:http: //geoff.evason.name/2010/04/23/nested-has_many-through-in-rails-or-how-to-do-a-3-table-join //

于 2011-07-12T14:12:04.817 に答える