我有一个模型,定义了100平方米到200平方米的尺寸范围。我写了一个验证器:
class SizeRange < ActiveRecord::Base
validate :non_overlapping
def non_overlapping
lower_in_range = SizeRange.where(lower_bound: lower_bound..upper_bound)
upper_in_range = SizeRange.where(upper_bound: lower_bound..upper_bound)
if lower_in_range.present?
errors.add(:lower_bound, 'blablabla')
end
if upper_in_range.present?
errors.add(:upper_bound, 'blablabla')
end
end
end
我的猜测是,当我尝试保存一个新的模型,其下限或上限似乎在另一个SizeRange实例的范围内时,验证器会将模型标记为无效,并且保存操作将被中止。 真正发生的是我的模型被保存并分配了一个id,但是当我调用model.valid时?它返回false(所以我的验证器似乎应该做它应该做的)。
有什么我可以做错的,或者我不明白验证器是如何工作的?我可以强制验证器中止保存操作吗?
另一个问题: 有没有办法通过数据库约束强制执行约束?我想我更喜欢数据库方面的解决方案。
感谢您的帮助!
答案 0 :(得分:1)
model.save
将被默默接受并返回false。它不会抛出任何异常。
你应该致电:
model.save!
验证失败
答案 1 :(得分:0)
每次出错后都应该返回false.add取消保存记录
答案 2 :(得分:0)
原来问题是我的验证器功能不完善。
我检查了一个导致混淆的案例:
SizeRange.new({:lower_bound=>250, :upper_bound=>350})
所以我希望它无效(model.valid? - &gt; false)但它当然是有效的。所以model.save没有回滚,我在model.valid上测试了吗? NOW会返回false,因为新保存的实例会违反约束,因为它是针对自身进行测试的。
所以有两个问题:
所以我最终重写了我的验证器功能:
def non_overlapping
sr = id.present? ? SizeRange.where.not(id: id) : SizeRange
ranges = sr.all
lower_overlappings = ranges.map { |r| lower_bound.between?(r.lower_bound, r.upper_bound)}
upper_overlappings = ranges.map { |r| upper_bound.between?(r.lower_bound, r.upper_bound)}
if lower_overlappings.any?
errors.add(:lower_bound, 'lower bla bla')
end
if upper_overlappings.any?
errors.add(:lower_bound, 'upper bla bla')
end
end
第一行处理第一个问题,其余部分处理预期的验证。
感谢您的帮助,并对此感到抱歉。
答案 3 :(得分:0)
你应该使用开始和救援:
class SizeRange < ActiveRecord::Base
validate :non_overlapping
private
def non_overlapping
lower_in_range = SizeRange.where(lower_bound: lower_bound..upper_bound)
upper_in_range = SizeRange.where(upper_bound: lower_bound..upper_bound)
begin
raise("Lower Bound Met!") if lower_in_range.present?
rescue => ex
errors.add(:lower_bound, "#{ex.message} (#{ex.class})")
end
begin
raise("Lower Bound Met!") if upper_in_range.present?
rescue => ex
errors.add(:upper_bound, "#{ex.message} (#{ex.class})")
end
end
end