2

我正在尝试更好地理解 Rails 数据库 API。如果我有以下型号:

class Link
  has_many :votes
  belongs_to :user
end

class Vote
  belongs_to: link
  belongs_to :user
end

class User
  has_many :links
  has_many :votes
end

我想“急切地”返回所有链接的列表,对于每个链接,我想要 user_id 等于当前用户的投票。我试过这个:

l = Link.all(:include => :votes, :conditions => { :votes => { :user_id => current_user.id}})

但这只会返回用户已提交投票的链接列表。我希望它返回所有链接,然后只返回该用户的投票(如果没有,则不返回)。如何使用 include 语句执行此操作?

4

1 回答 1

0

我不得不重新阅读这个问题几次,所以如果我对此感到困惑,请告诉我。听起来您想使用LEFT OUTER JOIN. 这将为您提供links投票与用户 ID 匹配的表中的所有内容,以及与当前用户没有投票匹配的所有其他链接。像这样的东西:

Link.all(:include => :votes, 
:select=>"DISTINCT links.*",
:joins => "LEFT OUTER JOIN votes ON links.id = votes.link_id AND votes.user_id = #{current_user.id}", 
:conditions=>"NOT EXISTS (SELECT * FROM votes WHERE votes.link_id = links.id) OR votes.user_id = #{current_user.id}")

此外,由于您已经在使用 Rails 3.2,您可能希望使用更新的活动记录查询方法,因为我相信旧的基于哈希的查找器方法在 Rails 4 中已弃用。所以相同的查询看起来像这样:

Link.include(:votes)
.joins("LEFT OUTER JOIN votes ON links.id = votes.link_id AND votes.user_id = #{current_user.id}")
.where("NOT EXISTS (SELECT * FROM votes WHERE votes.link_id = links.id) OR votes.user_id = #{current_user.id}")
.uniq.load

关于条件/位置部分,这将创建一个依赖子查询,如果该Link行存在于投票表中(意味着有人在某一时刻对其进行了投票),或者如果它确实存在,则该行将被拒绝。我们指定的用户。最终结果是您获得任何Link没有任何投票的行,或与指定用户创建的投票的链接。

更新

正如评论中提到的,这种方法的问题是关联的votes行没有按要求预先加载。在用这个工具处理了大约 2 个小时之后,我几乎要承认我不相信用这种查询来急切地加载关联的表是可能的。

如果我们放弃该include部分(这似乎无论如何都没有影响),我们确实会返回一组Link已由指定用户投票的对象,或者根本没有人投票。问题是当我们调用 时.votes Rails 很高兴地按照SELECT votes.* FROM votes WHERE votes.link_id = [the link id]

我能想到的唯一解决方案是在调用关联方法时正确量化您正在寻找的内容。像这样的东西:

link.votes.where(:user_id=>current_user.id)

这将产生这样的查询:

SELECT votes.* FROM votes WHERE votes.link_id = [the link ID] AND user_id = [the user ID]

如果有人有更好的解决方案,我有兴趣看到它。

希望有帮助。

于 2013-10-16T00:56:58.603 回答