验证多列的唯一性


193

有没有一种验证实际记录是唯一的而不仅仅是一列的方法?例如,友谊模型/表格不应具有多个相同的记录,例如:

user_id: 10 | friend_id: 20
user_id: 10 | friend_id: 20

7
如果我很稠密,请原谅我,但是在这种情况下有什么帮助?
re5et 2011年

2
尝试在模型中使用“ validates_uniqueness_of”。如果这不起作用,请尝试创建一个索引,在该索引上创建可以迁移的字段,其中包括如下语句:add_index:table,[:column_a,:column_b],:unique => true)
Harry Joy

1
@HarryJoy,他问Is there a rails-way way。而且您为他提供了无轨方式,但是很标准。The Active Record way claims that intelligence belongs in your models, not in the database.
格林

2
不幸的validates :field_name, unique: true是容易出现比赛条件,因此即使遇到铁轨,还是要有实际的约束。@HarryJoy我将投票回答一个描述约束方式的答案。
Pooyan Khosravi 2014年

1
更好的答案是,下面所有提到的就是这个stackoverflow.com/a/34425284/1612469,因为它带来了另一层来确保一切正常工作
Aleks

Answers:


319

您可以validates_uniqueness_of按如下方式确定通话范围。

validates_uniqueness_of :user_id, :scope => :friend_id

83
只是想补充一下,如果您需要验证两个以上字段的唯一性,则可以传递多个范围参数。即:scope => [:: friend_id,:group_id]
戴夫·

27
奇怪的是你不能说validates_uniqueness_of [:user_id, :friend_id]。也许需要修补?
Alexey'7

12
Alexey,validates_uniqueness_of [:user_id,:friend_id]只会对列出的每个字段进行验证-这是有据可查的,并且是预期的行为
Nikita Hismatov

71
在Rails 4中,它变为:验证:user_id,唯一性:{scope::friend_id}
Marina Martin

3
您可能想添加一个自定义错误消息,例如,:message =>'已经有这个朋友了。
laffuste 2014年

137

您可以使用在一列上validates进行验证uniqueness

validates :user_id, uniqueness: {scope: :friend_id}

验证多列的语法相似,但是您应该提供一个字段数组:

validates :attr, uniqueness: {scope: [:attr1, ... , :attrn]}

但是,上面显示的验证方法具有竞争条件,不能确保一致性。考虑以下示例:

  1. 数据库表记录应该由n个字段唯一;

  2. 多个(两个或多个)并发请求,分别由单独的进程(应用程序服务器,后台工作服务器或您正在使用的任何服务器)处理,它们访问数据库以在表中插入相同的记录;

  3. 每个并行处理都会验证是否存在具有相同n个字段的记录;

  4. 每个请求的验证均成功传递,并且每个进程在表中创建具有相同数据的记录。

为了避免这种行为,应该向数据库表添加唯一约束。您可以add_index通过运行以下迁移,使用帮助程序为一个(或多个)字段设置它:

class AddUniqueConstraints < ActiveRecord::Migration
  def change
   add_index :table_name, [:field1, ... , :fieldn], unique: true
  end
end

警告:即使设置了唯一约束,两个或多个并发请求仍将尝试将相同的数据写入db,但是这将引发一个ActiveRecord::RecordNotUnique异常,而不是创建重复的记录,应单独处理:

begin
# writing to database
rescue ActiveRecord::RecordNotUnique => e
# handling the case when record already exists
end 

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.