validation多列的唯一性

是否有轨道的方式来validation实际logging是唯一的,而不只是一列? 例如,一个友谊模型/表格应该不能有多个相同的logging,例如:

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

您可以按如下所示范围validates_uniqueness_of调用。

 validates_uniqueness_of :user_id, :scope => :friend_id 

您可以使用validates来validation一列上的uniqueness

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

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

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

但是 ,上面显示的validation方法存在争用条件,无法确保一致性。 考虑下面的例子:

  1. 数据库表logging应该是由n个字段唯一的;

  2. 多个( 两个或多个 )并发请求,由各自独立的进程( 应用程序服务器,后台工作服务器或您正在使用的任何其他进程)处理,访问数据库以在表中插入相同的logging;

  3. 每个进程并行validation是否存在具有相同n个字段的logging;

  4. 每个请求的validation都会成功传递,并且每个进程都会在表中使用相同的数据创build一条logging。

为了避免这种行为,应该为db表添加一个唯一的约束 。 您可以通过运行以下迁移来使用add_index助手为一个(或多个)字段进行设置:

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

注意 :即使设置了一个唯一的约束,两个或多个并发请求也会尝试向db写入相同的数据,但是不会创build重复的logging,这会引发一个ActiveRecord::RecordNotUniqueexception,您应该单独处理:

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

你可能确实需要对数据库的实际约束,因为validation受到竞争条件的影响。

 validates_uniqueness_of :user_id, :scope => :friend_id 

当你坚持一个用户实例时,Rails将通过运行一个SELECT查询来validation你的模型,看看是否有用户logging已经存在与提供的user_id。 假设loggingcertificate是有效的,Rails将运行INSERT语句来坚持​​用户。 如果运行单个进程/线程Web服务器的单个实例,这非常有效。

如果两个进程/线程在同一时间尝试创build具有相同user_id的用户,则可能出现以下情况。 竞赛条件与验证

在分贝上有独特的索引,上面的情况将如下展示。 数据库的唯一索引

答复从这个博客文章 – http://robots.thoughtbot.com/the-perils-of-uniqueness-validations

数据库约束:

add_index :friendships, [:user_id, :friend_id], unique: true