Ruby-on-rails 验证多列的唯一性

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/4870961/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-03 00:17:15  来源:igfitidea点击:

Validate uniqueness of multiple columns

ruby-on-railsruby-on-rails-3validationactiverecordrails-activerecord

提问by re5et

Is there a rails-way way to validate that an actual record is unique and not just a column? For example, a friendship model / table should not be able to have multiple identical records like:

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

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

回答by Dylan Markow

You can scope a validates_uniqueness_ofcall as follows.

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

validates_uniqueness_of :user_id, :scope => :friend_id

回答by potashin

You can use validatesto validate uniquenesson one column:

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

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

The syntax for the validation on multiple columns is similar, but you should provide an array of fields instead:

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

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


However, validation approaches shown above have a race condition and can't ensure consistency. Consider the following example:

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

  1. database table records are supposed to be unique by nfields;

  2. multiple (two or more) concurrent requests, handled by separate processes each (application servers, background worker servers or whatever you are using), access database to insert the same record in table;

  3. each process in parallel validates if there is a record with the same nfields;

  4. validation for each request is passed successfully, and each process creates a record in the table with the same data.

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

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

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

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

To avoid this kind of behaviour, one should add a unique constraintto db table. You can set it with add_indexhelper for one (or multiple) field(s) by running the following migration:

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

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

Caveat: even after you've set a unique constraint, two or more concurrent requests will try to write the same data to db, but instead of creating duplicate records, this will raise an ActiveRecord::RecordNotUniqueexception, which you should handle separately:

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

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

回答by Tate Thurston

This can be done with a database constraint on the two columns:

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

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

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

You could use a rails validator, but in general I recommend using a database constraint.

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

More reading: https://robots.thoughtbot.com/validation-database-constraint-or-both

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