203

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

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

3 回答 3

322

您可以validates_uniqueness_of按如下方式确定调用范围。

validates_uniqueness_of :user_id, :scope => :friend_id
于 2011-02-02T05:46:12.970 回答
151

您可以validates用来验证uniqueness一列:

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

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

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

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

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

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

  3. 每个进程并行验证是否存在具有相同n 个字段的记录;

  4. 每个请求的验证都成功通过,每个进程在表中创建一条具有相同数据的记录。

为避免这种行为,应向db 表添加唯一约束。您可以add_index通过运行以下迁移为一个(或多个)字段设置它:

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

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

begin
# writing to database
rescue ActiveRecord::RecordNotUnique => e
# handling the case when record already exists
end 
于 2015-04-30T07:23:21.153 回答
2

这可以通过对两列的数据库约束来完成:

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

您可以使用 rails 验证器,但通常我建议使用数据库约束。

更多阅读:https ://robots.thoughtbot.com/validation-database-constraint-or-both

于 2017-08-30T21:53:50.980 回答