如何将这个模型验证写为SQL约束?



我在模型中对number属性进行了验证,并且我还想添加一个数据库约束。你会怎么做呢?您会添加索引或部分索引来满足作用域和if语句吗?

validates :number,
numericality: { greater_than: 0 },
uniqueness: { scope: :tenant_id },
unless: state == 'pending',
if: :number

我正在考虑这个迁移来创建约束但我不确定这是最好的方法

def change
reversible do |dir| 
dir.up do execute <<-SQL
ALTER TABLE work_orders
ADD CONSTRAINT number_uniqueness_constraint
CHECK ( number IS NULL OR number NOT IN
( SELECT number
FROM work_orders
WHERE tenant_id = work_orders.tenant_id )
)
SQL
end
end
end

业务约束既应该在数据库中定义为硬约束(如果可能的话),也应该在Rails中定义。

在数据库中拥有约束的好处是,这严格确保了您的数据集是有效的,因为数据库不允许打破约束。然而,这样做的缺点是,如果有一个查询试图打破约束,错误消息将不会是一个很好的用户体验。

在Rails中,您可以为每个约束获得很好的消息,并且可以很好地处理错误。这对于格式约束(如数字检查)非常有效。然而,对于唯一性约束,存在竞争条件的可能性,因为在并行请求中,唯一性检查和随后的提交可能重叠,导致约束失败。

因此,在两个地方都定义约束通常是一个好主意。在大多数情况下,Rails约束将产生良好的错误消息,SQL约束将严格确保数据有效,即使存在竞争。

话虽这么说,你的Rails约束目前不匹配你的Postgres约束。你错过了unless规则。对于唯一性约束,您还可以为这两列使用更简单的组合UNIQUE约束,而不是编写自己的子查询,这取决于您计划如何精确地解决边缘情况(即Rails验证的unlessif规则)。

最新更新