Rails:包括所有模型A和仅相关模型B的子集
我想更好地理解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语句执行此操作?
我不得不重读这个问题几次,所以如果我对此感到困惑,请告诉我。 听起来你想要使用LEFT OUTER JOIN
。 这将为您提供投票与用户ID匹配的links
表中的所有内容,以及与当前用户没有投票匹配的所有其他链接。 像这样的东西:
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
关于条件/ where部分,这将创建一个从属子查询,如果它存在于投票表中(这意味着SOMEBODY在某一点上投票),将导致Link
行被拒绝,或者它是否确实存在是否是投票由我们指定的用户。 最终结果是您获得任何没有任何投票的Link
行,或者获得由指定用户创建的投票的链接。
UPDATE
正如评论中所提到的,这种方法的问题在于,关联的votes
行不会根据需要加载。 在使用了大约2个小时的工具后,我几乎要承认我不相信可以通过这种查询来加载关联表。
如果我们抛弃include
部分(似乎无论如何都没有效果),我们会找回已由指定用户投票的Link
对象集合,或者根本没有人。 问题是当我们调用.votes
,Rails愉快地按照SELECT votes.* FROM votes WHERE votes.link_id = [the link id]
再次调用数据库 SELECT votes.* FROM votes WHERE votes.link_id = [the link id]
,正如你所看到的那样,不会被user_id区别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]
如果有人有更好的解决方案,我会有兴趣看到它。
希望有所帮助。