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方法存在争用条件,无法确保一致性。 考虑下面的例子:
-
数据库表logging应该是由n个字段唯一的;
-
多个( 两个或多个 )并发请求,由各自独立的进程( 应用程序服务器,后台工作服务器或您正在使用的任何其他进程)处理,访问数据库以在表中插入相同的logging;
-
每个进程并行validation是否存在具有相同n个字段的logging;
-
每个请求的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::RecordNotUnique
exception,您应该单独处理:
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