ruby on rails - how - Ruby-on-Rails: Multiple has_many: a través de posible?
rails foreign key (4)
¿Es posible tener múltiples has_many :through
relaciones que se cruzan en Rails? Recibí la sugerencia de hacerlo como una solución para otra pregunta que publiqué, pero no pude hacerlo funcionar.
Los amigos son una asociación cíclica a través de una tabla de unión. El objetivo es crear un has_many :through
de friends_comments
, para que pueda tomar un User
y hacer algo como user.friends_comments
para obtener todos los comentarios realizados por sus amigos en una sola 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
Esto se ve muy bien, y tiene sentido, pero no funciona para mí. Este es el error que recibo en la parte relevante cuando intento acceder a los comentarios de amigos de un usuario:
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)))
Cuando acabo de ingresar user.friends, que funciona, esta es la consulta que ejecuta:
: SELECT "users".* FROM "users" INNER JOIN "friendships" ON "users".id = "friendships".friend_id WHERE (("friendships".user_id = 1) AND ((status = 2)))
Por lo tanto, parece que se está olvidando por completo de la relación original has_many
través de la amistad, y luego está tratando de usar inapropiadamente la clase User como una tabla de unión.
¿Estoy haciendo algo mal o simplemente no es posible?
Aunque esto no funcionó en el pasado, ahora funciona bien en Rails 3.1.
Encontré esta entrada de blog útil: http://geoff.evason.name/2010/04/23/nested-has_many-through-in-rails-or-how-to-do-a-3-table-join/
Hay un complemento que resuelve su problema, eche un vistazo a este blog .
Instala el plugin con
script/plugin install git://github.com/ianwhite/nested_has_many_through.git
Editar:
Rails 3.1 soporta asociaciones anidadas. P.ej:
has_many :tasks
has_many :assigments, :through => :tasks
has_many :users, :through => :assignments
No hay necesidad de la solución dada a continuación. Consulte this screencast para más detalles.
Respuesta original
Estás pasando un has_many :through
asociación como fuente para otro has_many :through
asociación. No creo que funcione.
has_many :friends,
:through => :friendships,
:conditions => "status = #{Friendship::FULL}"
has_many :friends_comments, :through => :friends, :source => :comments
Tienes tres enfoques para resolver este problema.
1) Escribir una extensión de asociación
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
Ahora puedes obtener los comentarios de los amigos de la siguiente manera:
user.friends.comments
2) Agregar un método a la clase de User
.
def friends_comments(reload=false)
@friends_comments = nil if reload
@friends_comments ||=Comment.find_all_by_user_id(self.friend_ids)
end
Ahora puedes obtener los comentarios de los amigos de la siguiente manera:
user.friends_comments
3) Si quieres que esto sea aún más eficiente, entonces:
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
Ahora puedes obtener los comentarios de los amigos de la siguiente manera:
user.friends_comments
Todos los métodos almacenan en caché los resultados. Si quieres recargar los resultados haz lo siguiente:
user.friends_comments(true)
user.friends.comments(true)
O mejor aún:
user.friends_comments(:reload)
user.friends.comments(:reload)