Ruby-on-Rails: has_many diverses formes: possible?
-
24-09-2019 - |
Question
Est-il possible d'avoir plusieurs relations has_many :through
qui passent par l'autre dans Rails? J'ai reçu la suggestion de le faire comme une solution pour une autre question que j'ai posté, mais ont été incapables de le faire fonctionner.
Les amis sont une association cyclique à travers une table de jointure. L'objectif est de créer un has_many :through
pour friends_comments
, donc je peux prendre un User
et faire quelque chose comme user.friends_comments
pour obtenir tous les commentaires de 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 semble grande et logique, mais ne fonctionne pas pour moi. Ceci est l'erreur que je reçois dans la partie pertinente lorsque je tente d'accéder aux friends_comments d'un utilisateur:
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)))
Quand je rentre juste user.friends, qui fonctionne, c'est la requête qu'il exécute:
: SELECT "users".* FROM "users" INNER JOIN "friendships" ON "users".id = "friendships".friend_id WHERE (("friendships".user_id = 1) AND ((status = 2)))
Il semble donc que c'est tout à fait oublier la has_many
originale à travers une relation d'amitié, et est ensuite essayer d'utiliser de façon inappropriée la classe utilisateur comme une table de jointure.
Ai-je fait quelque chose de mal, ou est-ce tout simplement pas possible?
La solution
Modifier
Rails 3.1 prend en charge les associations imbriquées. Par exemple:
has_many :tasks
has_many :assigments, :through => :tasks
has_many :users, :through => :assignments
Il n'y a pas besoin de la solution ci-dessous. Reportez-vous à ce screencast pour plus de détails.
Réponse originale
Vous passez une association de has_many :through
comme source pour une autre has_many :through
association. Je ne pense pas que cela fonctionnera.
has_many :friends,
:through => :friendships,
:conditions => "status = #{Friendship::FULL}"
has_many :friends_comments, :through => :friends, :source => :comments
Vous avez trois approches pour résoudre ce problème.
1) une extension d'association
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 des commentaires les amis comme suit:
user.friends.comments
2) Ajouter une méthode à la classe User
.
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 des commentaires les amis comme suit:
user.friends_comments
3) Si vous voulez que ce soit alors encore plus efficace:
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 des commentaires les amis comme suit:
user.friends_comments
Toutes les méthodes cache les résultats. Si vous voulez recharger les résultats procédez comme suit:
user.friends_comments(true)
user.friends.comments(true)
Ou mieux encore:
user.friends_comments(:reload)
user.friends.comments(:reload)
Autres conseils
Il y a un plugin qui permet de résoudre votre problème, jetez un oeil à ce blog .
Vous installez le plug-in avec
script/plugin install git://github.com/ianwhite/nested_has_many_through.git
Bien que cela ne fonctionne pas dans le passé, il fonctionne très bien dans Rails 3.1 maintenant.
J'ai trouvé cette entrée de blog pour être utile: http://geoff.evason.name/2010/04/23/nested-has_many-through-in-rails-or-how-to-do- a-3-table join /