【发布时间】:2018-06-01 00:08:40
【问题描述】:
当结合 has_one 关联和 includes 时,我一直试图找出一些奇怪的行为。
class Post < ApplicationRecord
has_many :comments
has_one :latest_comment, -> { order('comments.id DESC').limit(1) }, class_name: 'Comment'
end
class Comment < ApplicationRecord
belongs_to :post
end
为了测试这一点,我创建了两个帖子,每个帖子都有两个 cmets。下面是一些显示奇怪行为的 rails 控制台命令。当我们使用includes 时,它会忽略latest_comment 关联的顺序。
posts = Post.includes(:latest_comment).references(:latest_comment)
posts.map {|p| p.latest_comment.id}
=> [1, 3]
posts.map {|p| p.comments.last.id}
=> [2, 4]
我希望这些命令具有相同的输出。 posts.map {|p| p.latest_comment.id} 应该返回 [2, 4]。由于 n+1 查询问题,我无法使用第二个命令。
如果您单独调用最新评论(类似于上面的comments.last),那么一切都会按预期进行。
[Post.first.latest_comment.id, Post.last.latest_comment.id]
=> [2, 4]
如果您有其他方法可以实现此行为,我欢迎您提出意见。这让我很困惑。
【问题讨论】:
-
您似乎使用了错误的顺序。您使用
ASC但您的期望使用p.comments.last.id的范围实际上是DESC订单 -
我认为你不能那样做。您的
has_one的scope参数最终出现在查询产生的 LEFT JOIN 的连接条件中(请参阅Post.includes(:latest_comment).references(:latest_comment).to_sql),并且 ORDER BY 在连接条件中没有任何意义,因此 AR 不会费心将其放入。如果您尝试使用 WHERE 子句(将在连接条件中)使范围实例依赖 (->(post) { ... }) 以获取最新的,那么您将收到 ArgumentError。 -
@yeuem1vannam - 是的,应该是 DESC。我更新了。
-
@muistooshort - 是的。这一切都说得通。有没有其他方法可以做到这一点?这是我现在的解决方法,
Post.left_joins(:latest_comment).distinct -
你有点滥用
has_one及其范围参数,所以它只是部分工作。您使用的是哪个数据库?
标签: ruby-on-rails postgresql activerecord rails-activerecord