Ruby on Rails:多has_many:通过可能吗?
在Rails中可以有多个穿越彼此的has_many :through
关系吗?我收到了这样一个建议,作为我发布的另一个问题的解决方案,但一直无法实现。Ruby on Rails:多has_many:通过可能吗?
朋友是循环关联通过一个连接表。我们的目标是为friends_comments
创建一个has_many :through
,因此我可以采取User
并执行类似user.friends_comments
的操作,以便在单个查询中获取其朋友发出的所有评论。
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
这看起来不错,有道理,但不适合我。这是我收到的相关部分的错误,当我试图访问用户的friends_comments: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)))
当我刚进入user.friends,它的工作原理,这是它执行查询:: SELECT "users".* FROM "users" INNER JOIN "friendships" ON "users".id = "friendships".friend_id WHERE (("friendships".user_id = 1) AND ((status = 2)))
因此,它似乎通过友谊关系完全忘记了原始的has_many
,然后不恰当地尝试使用User类作为连接表。
我做错了什么,或者这是不可能的?
编辑:
的Rails 3.1支持嵌套关联。例如:
has_many :tasks
has_many :assigments, :through => :tasks
has_many :users, :through => :assignments
不需要下面给出的解决方案。有关更多详细信息,请参阅this截屏视频。
原来的答案
你传递一个has_many :through
协会作为源另一has_many :through
关联。我不认为它会起作用。
has_many :friends,
:through => :friendships,
:conditions => "status = #{Friendship::FULL}"
has_many :friends_comments, :through => :friends, :source => :comments
有三种方法可以解决这个问题。
1)写一个协会扩展
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
现在你可以得到朋友们的意见如下:
user.friends.comments
2)添加一个方法到User
类。
def friends_comments(reload=false)
@friends_comments = nil if reload
@friends_comments ||=Comment.find_all_by_user_id(self.friend_ids)
end
现在你可以得到朋友们的意见如下:
user.friends_comments
3)如果你想这是更有效的,那么:
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
现在你可以得到朋友意见如下:
user.friends_comments
所有方法都缓存结果。如果你想重新加载结果执行以下操作:
user.friends_comments(true)
user.friends.comments(true)
或者更好的是:
user.friends_comments(:reload)
user.friends.comments(:reload)
有是解决你的问题的一个插件,看看this blog。
您安装与
script/plugin install git://github.com/ianwhite/nested_has_many_through.git
虽然这并没有在过去的工作,它工作在Rails的3.1现在罚款。
不幸的是,这种方法背后的原始目的是让我可以在一个查询中获得来自SQL的所有朋友评论。见这里: http://stackoverflow.com/questions/2382642/ruby-on-rails-how-to-pull-out-most-recent-entries-from-a-limited-subset-of-a-dat 如果我编写一个函数,这将导致N + 1个查询。 – 2010-03-04 23:56:55
不,它不会。这将是2个查询。第一个查询让朋友和第二个查询获得所有**朋友的评论。如果您已将朋友加载到用户模型中,则不会产生任何费用。我已经用两种方法更新了解决方案。看一看。 – 2010-03-05 00:17:09
@williamjones您在这三种方法中都没有N + 1问题。您可以检查您的日志文件以确保这一点。我个人喜欢方法1,因为它非常优雅,即'user.friends.comments'优于'user.friends_comments' – 2010-03-05 00:35:21