55 votes

Ruby-on-Rails: Multiple has_many: possible?

Est-il possible d'avoir plusieurs has_many :par le biais de relations qui traversent les uns les autres dans les Rails? J'ai reçu la suggestion de le faire comme une solution pour l'autre question que j'ai posté, mais ont été incapables de le faire fonctionner.

Les amis sont une reprise cyclique de l'association grâce à une table de jointure. L'objectif est de créer un has_many :through pour friends_comments, afin que je puisse prendre de l'Utilisateur et de faire quelque chose comme utilisateur.friends_comments pour obtenir toutes les observations faites par ses amis dans une seule requête.

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

Cela ressemble beaucoup, et du bon sens, mais ne fonctionne pas pour moi. C'est l'erreur que je reçois dans la partie pertinente lorsque j'essaie d'accéder à un utilisateur friends_comments:
ERROR: column users.user_id does not exist

Quand je viens de l'entrée de l'utilisateur.des amis, qui fonctionne, c'est de la requête qu'il exécute:

Donc il semble que c'est tout à fait oublier à propos de l'origine has_many grâce à l'amitié de relation, est inappropriée d'essayer d'utiliser la classe Utilisateur une table de jointure.

Suis-je en train de faire quelque chose de mal, ou est-ce tout simplement pas possible?

76voto

Harish Shetty Points 38877

Edit:

Rails 3.1 prend en charge imbriqués les associations. E. g:

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

Il n'est pas nécessaire pour la solution donnée ci-dessous. Reportez-vous à ce screencast pour plus de détails.

Réponse Originale À Cette Question

Vous êtes de passage à un has_many :through association comme source pour une autre has_many :through de l'association. Je ne pense pas que cela va fonctionner.

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

Vous avez trois approches pour la résolution de ce problème.

1) Écrire une association de vulgarisation

 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

Maintenant, vous pouvez obtenir les amis des commentaires comme suit:

user.friends.comments

2) Ajouter une méthode à l' User classe.

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

Maintenant, vous pouvez obtenir les amis des commentaires comme suit:

user.friends_comments

3) Si vous voulez que ce soit encore plus efficace, alors:

  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

Maintenant, vous pouvez obtenir les amis des commentaires comme suit:

user.friends_comments

Toutes les méthodes à mettre en cache les résultats. Si vous souhaitez recharger les résultats suivants:

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

OU mieux encore:

user.friends_comments(:reload)
user.friends.comments(:reload)

8voto

Jarl Points 1243

Il existe un plugin qui résout votre problème, jetez un coup d'œil à ce blog .

Vous installez le plugin avec

 script/plugin install git://github.com/ianwhite/nested_has_many_through.git
 

- Jarl

5voto

Andrew Culver Points 1010

Bien que cela n'ait pas fonctionné dans le passé, cela fonctionne désormais bien dans Rails 3.1.

3voto

Bryan Larsen Points 2630

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X