Ruby-on-Rails: Múltiplo Has_Many: Através do possível?
-
24-09-2019 - |
Pergunta
É possível ter múltiplos has_many :through
relacionamentos que passam um pelo outro em trilhos? Recebi a sugestão de fazê -lo como uma solução para outra pergunta que publiquei, mas não consegui fazê -la funcionar.
Amigos são um Associação Cíclica através de uma tabela de junção. O objetivo é criar um has_many :through
por friends_comments
, então eu posso levar um User
e fazer algo como user.friends_comments
Para obter todos os comentários feitos por seus amigos em uma única consulta.
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
Isso parece ótimo e faz sentido, mas não está funcionando para mim. Este é o erro que estou recebendo em uma parte relevante quando tento acessar o Friends_comments de um usuário:
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)))
Quando eu apenas entro no user.friends, que funciona, esta é a consulta que ele executa:
: SELECT "users".* FROM "users" INNER JOIN "friendships" ON "users".id = "friendships".friend_id WHERE (("friendships".user_id = 1) AND ((status = 2)))
Parece que está esquecendo inteiramente o original has_many
Através do relacionamento de amizade, e depois está inapropriadamente tentando usar a classe de usuário como uma tabela de junção.
Estou fazendo algo errado, ou isso simplesmente não é possível?
Solução
Editar:
O Rails 3.1 suporta associações aninhadas. Por exemplo:
has_many :tasks
has_many :assigments, :through => :tasks
has_many :users, :through => :assignments
Não há necessidade da solução fornecida abaixo. Referir-se isto Screencast para mais detalhes.
Resposta original
Você está passando um has_many :through
associação como fonte para outro has_many :through
Associação. Eu não acho que vai funcionar.
has_many :friends,
:through => :friendships,
:conditions => "status = #{Friendship::FULL}"
has_many :friends_comments, :through => :friends, :source => :comments
Você tem três abordagens para resolver esse problema.
1) Escreva uma extensão de associação
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
Agora você pode receber os comentários dos amigos da seguinte forma:
user.friends.comments
2) Adicione um método ao User
classe.
def friends_comments(reload=false)
@friends_comments = nil if reload
@friends_comments ||=Comment.find_all_by_user_id(self.friend_ids)
end
Agora você pode receber os comentários dos amigos da seguinte forma:
user.friends_comments
3) Se você deseja que isso seja ainda mais eficiente do que:
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
Agora você pode receber os comentários dos amigos da seguinte forma:
user.friends_comments
Todos os métodos cache os resultados. Se você deseja recarregar os resultados, faça o seguinte:
user.friends_comments(true)
user.friends.comments(true)
Ou melhor ainda:
user.friends_comments(:reload)
user.friends.comments(:reload)
Outras dicas
Há um plugin que resolve seu problema, dê uma olhada em este blog.
Você instala o plugin com
script/plugin install git://github.com/ianwhite/nested_has_many_through.git
Embora isso não tenha funcionado no passado, funciona bem no Rails 3.1 agora.
Eu achei essa entrada de blog útil: http://geoff.evason.name/2010/04.