我创建了一个验证规则来限制成员可以创建的记录数。
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
我做错了什么?只有连接才是同一个查询?
答案 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
。