Rails 3:has_many:uniq问题

时间:2011-03-07 14:07:40

标签: ruby-on-rails ruby-on-rails-3

这是一个示例模型设置(准系统Rails 3.0.5):

class Post < ActiveRecord::Base  
  has_many :comments
end

class Comment < ActiveRecord::Base  
  belongs_to :post
  belongs_to :user
end

class User < ActiveRecord::Base
  has_many :comments
  has_many :commented_posts, through: :comments, source: :post, uniq: true
end

现在以下工作正常:

ruby-1.9.2-p0 > user.commented_posts.count
  SQL (0.2ms)  SELECT COUNT(DISTINCT "posts".id) FROM "posts" INNER JOIN "comments" ON "posts".id = "comments".post_id WHERE (("comments".user_id = 1))
=> 1

但是添加条件使得活动记录'忘记'关于uniq: true位:

ruby-1.9.2-p0 > user.commented_posts.where("posts.id != 42").count
  SQL (0.2ms)  SELECT COUNT(*) FROM "posts" INNER JOIN "comments" ON "posts".id = "comments".post_id WHERE (("comments".user_id = 1)) AND (posts.id != 42)
=> 2

错误?或者我错过了什么?

修改

all有效:

ruby-1.9.2-p0 > user.commented_posts.where("posts.id != 42").all
  Post Load (0.3ms)  SELECT DISTINCT "posts".* FROM "posts" INNER JOIN "comments" ON "posts".id = "comments".post_id WHERE (("comments".user_id = 1)) AND (posts.id != 42)
=> [#<Post id: 1, created_at: "2011-03-07 12:17:30", updated_at: "2011-03-07 12:17:30">] 

显式uniq

ruby-1.9.2-p0 > user.commented_posts.where("posts.id != 42").uniq.count
  Post Load (0.2ms)  SELECT DISTINCT "posts".* FROM "posts" INNER JOIN "comments" ON "posts".id = "comments".post_id WHERE (("comments".user_id = 1)) AND (posts.id != 42)
=> 1

编辑2

确实是Rails中的错误。我提交了一个补丁。请进行投票,以便更快通过。 https://github.com/rails/rails/pull/2924#issuecomment-3317185

2 个答案:

答案 0 :(得分:1)

我在3.0.9和3.0.10上都遇到了这个问题。我认为这是一个错误的bug,尽管可能有某些原因导致它的行为方式。

我尝试重写了连接方法的计数...

has_many :commented_posts, through: :comments, source: :post, uniq: true do
  def count(column_name = nil, opts = {})
    super(column_name || 'users.id', opts.reverse_merge(distinct: true))
  end
end

但是如果存在条件,则arel会忽略count方法。这就是为什么我认为这是一个错误。

作为黑客攻击解决方案,我使用@ comments.count('users.id',distinct:true)强制arel在@comments可能在控制器中附加条件的情况下运行。

答案 1 :(得分:0)

我认为显式uniq导致计数从内存中的数组发生,而不是在数据库中进行计数。您可以从SELECT DISTINCT而不是SELECT COUNT DISTINCT中判断这是真的。这会将数据库中的所有条目拉入轨道,然后计算它们。

可以通过将几个参数传递给count

来让DB完成此操作

collection.count(:id,:distinct =&gt; true)将导致生成正确的SQL代码。