Ruby-on-Rails: has_many multipla: attraverso possibile?
-
24-09-2019 - |
Domanda
E 'possibile avere più rapporti has_many :through
che attraversano l'un l'altro in Rails? Ho ricevuto il suggerimento di farlo come una soluzione per un'altra domanda che ho postato, ma non sono riuscito a farlo funzionare.
Gli amici sono un ciclica associazione attraverso un join tavolo. L'obiettivo è quello di creare un has_many :through
per friends_comments
, in modo da poter prendere una User
e fare qualcosa di simile user.friends_comments
per ottenere tutti i commenti inseriti dai suoi amici in una singola query.
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
Questo sembra grande, e ha un senso, ma non funziona per me. Questo è l'errore che sto ricevendo in parte rilevante quando provo ad accedere friends_comments di un utente:
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 ho appena entro in user.friends, che funziona, questa è la query esegue:
: SELECT "users".* FROM "users" INNER JOIN "friendships" ON "users".id = "friendships".friend_id WHERE (("friendships".user_id = 1) AND ((status = 2)))
Così sembra come se fosse del tutto dimenticare la has_many
originale attraverso rapporti di amicizia, e poi viene impropriamente cercando di utilizzare la classe di utente come un join tabella.
sto facendo qualcosa di sbagliato, o è semplicemente possibile?
Soluzione
Modifica
Rails 3.1 supporti associazioni nidificate. Per esempio:
has_many :tasks
has_many :assigments, :through => :tasks
has_many :users, :through => :assignments
Non è necessario per la soluzione indicato di seguito. Fare riferimento a questo screencast per ulteriori dettagli.
risposta originale
Si passa un'associazione has_many :through
come fonte per un altro has_many :through
associazione. Non credo che funzionerà.
has_many :friends,
:through => :friendships,
:conditions => "status = #{Friendship::FULL}"
has_many :friends_comments, :through => :friends, :source => :comments
Ci sono tre approcci per risolvere questo problema.
1) un'estensione associazione
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
Ora potete ottenere gli amici commenti come segue:
user.friends.comments
2) Aggiungere un metodo alla classe User
.
def friends_comments(reload=false)
@friends_comments = nil if reload
@friends_comments ||=Comment.find_all_by_user_id(self.friend_ids)
end
Ora potete ottenere gli amici commenti come segue:
user.friends_comments
3) Se si desidera che questo sia ancora più efficiente allora:
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
Ora potete ottenere gli amici commenti come segue:
user.friends_comments
Tutti i metodi di cache i risultati. Se si desidera ricaricare i risultati fanno il seguente:
user.friends_comments(true)
user.friends.comments(true)
O meglio ancora:
user.friends_comments(:reload)
user.friends.comments(:reload)
Altri suggerimenti
V'è un plugin che risolve il problema, dare un'occhiata a questo blog .
Si installa il plugin con
script/plugin install git://github.com/ianwhite/nested_has_many_through.git
Anche se questo non ha funzionato in passato, funziona benissimo in Rails 3.1 ora.
Ho trovato questo blog per essere utile: http://geoff.evason.name/2010/04/23/nested-has_many-through-in-rails-or-how-to-do- a-3-table-join /