Ruby on Rails 4与内连接不同

时间:2015-05-29 09:57:03

标签: ruby-on-rails ruby ruby-on-rails-4 activerecord

我创建了一个验证规则来限制成员可以创建的记录数。

class Engine < ActiveRecord::Base
  validates :engine_code, presence: true
  belongs_to :group
  delegate :member, to: :group

  validate :engines_within_limit, on: :create

  def engines_within_limit
    if self.member.engines(:reload).distinct.count(:engine_code) >= self.member.engine_limit
      errors.add(:engine, "Exceeded engine limit")
    end
  end
end

以上不起作用,特别是这部分,

self.member.engines(:reload).distinct.count(:engine_code)

它产生的查询是

SELECT "engines".* 
FROM "engines" 
INNER JOIN "groups" 
ON "engines"."group_id" = "groups"."id" 
WHERE "groups"."member_id" = $1  [["member_id", 22]]

并返回错误的计数0

以下

Engine.distinct.count(:engine_code)

生成查询

SELECT DISTINCT COUNT(DISTINCT "engines"."engine_code") 
FROM "engines"

并返回正确的3

我做错了什么?只有连接才是同一个查询?

2 个答案:

答案 0 :(得分:3)

执行long chat后,我们发现以下查询有效:

self.member
    .engines(:reload)
    .count("DISTINCT engine_code")

答案 1 :(得分:1)

AR::表示ActiveRecord::以下。

问题中“错误”结果的原因是集合关联使用不正确。记录的集合关联(例如has_many)不是AR::Relation它是AR::Associations::CollectionProxy。它是AR::Relation的子类,例如distinct被覆盖。

self.member.engines(:reload).distinct.count(:engine_code)会导致这种情况发生:

  • self.member.engines(:reload)是一个 AR::Associations::CollectionProxy
  • 首先是
  • .distinct 触发db读取,然后对结果执行.to_a然后执行 “它是自己的”distinct,它在记录数组上执行uniq 关于记录的身份。
    结果是一个数组。

  • .count(:engine_code)这对正在返回的数组执行Array#count 0因为数组中没有记录等于符号:engine_code

要获得正确的结果,您应该使用关联代理的relation .scope
self.member.engines(:reload).scope.distinct.count(:engine_code)

我认为在Rails中如何处理集合关联有点令人困惑。许多关系的“正常”方法照常工作,例如:这将在不使用.scope的情况下工作:
self.member.engines(:reload).where('true').distinct.count(:engine_code)
这是因为where没有覆盖AR::Associations::CollectionProxy 也许在将集合用作关系时总是必须使用.scope