验证多列的唯一性
Posted
技术标签:
【中文标题】验证多列的唯一性【英文标题】:Validate uniqueness of multiple columns 【发布时间】:2011-06-19 17:58:29 【问题描述】:是否有一种方法可以验证实际记录是唯一的,而不仅仅是一列?例如,一个友谊模型/表不应该有多个相同的记录,例如:
user_id: 10 | friend_id: 20
user_id: 10 | friend_id: 20
【问题讨论】:
尝试在您的模型中使用“validates_uniqueness_of”。如果这不起作用,请尝试创建一个索引,您可以在该索引上创建 feilds 迁移,其中包括 add_index :table, [:column_a, :column_b], :unique => true) 之类的语句 不幸的是,validates :field_name, unique: true
容易出现竞争条件,因此即使反对 rails-way,实际约束也是首选。 @HarryJoy 我会赞成一个描述约束方式的答案。
@Green 公平地说,这是确保它永远不会发生的好方法。可以绕过验证。
比下面提到的更好的答案是***.com/a/34425284/1612469,因为它带来了另一层以确保一切正常
【参考方案1】:
您可以按如下方式确定validates_uniqueness_of
调用的范围。
validates_uniqueness_of :user_id, :scope => :friend_id
【讨论】:
只是想补充一点,您可以传递多个范围参数,以防您需要验证超过 2 个字段的唯一性。 IE。 :scope => [:friend_id, :group_id] 奇怪的是你不能说validates_uniqueness_of [:user_id, :friend_id]
。也许这需要修补?
Alexey, validates_uniqueness_of [:user_id, :friend_id] 只会对列出的每个字段进行验证 - 并且记录在案和预期的行为
在 Rails 4 中,这变成: validates :user_id, uniqueness: scope: :friend_id
您可能想添加一个自定义错误消息,例如 :message => ' has already this friend.'【参考方案2】:
您可以使用validates
在一列上验证uniqueness
:
validates :user_id, uniqueness: scope: :friend_id
多列验证的语法类似,但您应该提供一个字段数组:
validates :attr, uniqueness: scope: [:attr1, ... , :attrn]
然而,上面显示的验证方法存在竞争条件,无法确保一致性。考虑以下示例:
数据库表记录在n个字段中应该是唯一的;
多个(两个或更多)并发请求,分别由单独的进程(应用程序服务器、后台工作服务器或您正在使用的任何东西)处理,访问数据库在表中插入相同的记录;
每个进程并行验证是否存在具有相同n个字段的记录;
每个请求的验证都成功通过,每个进程在表中创建一条数据相同的记录。
为避免这种行为,应向 db 表添加一个唯一约束。您可以通过运行以下迁移为一个(或多个)字段使用 add_index
助手设置它:
class AddUniqueConstraints < ActiveRecord::Migration
def change
add_index :table_name, [:field1, ... , :fieldn], unique: true
end
end
警告:即使您设置了唯一约束,两个或多个并发请求也会尝试将相同的数据写入 db,但不会创建重复记录,而是会引发 @987654323 @ 异常,应该单独处理:
begin
# writing to database
rescue ActiveRecord::RecordNotUnique => e
# handling the case when record already exists
end
【讨论】:
【参考方案3】:这可以通过对两列的数据库约束来完成:
add_index :friendships, [:user_id, :friend_id], unique: true
您可以使用 rails 验证器,但通常我建议使用数据库约束。
更多阅读:https://robots.thoughtbot.com/validation-database-constraint-or-both
【讨论】:
以上是关于验证多列的唯一性的主要内容,如果未能解决你的问题,请参考以下文章