这是唯一索引和validates_uniqueness_of之间的区别
这是一个补丁,使 ActiveRecord 能够识别数据库生成的唯一约束违规错误。例如,它可以在不声明 validates_uniqueness_of 的情况下完成以下工作:
create_table "users" do |t|
t.string "email", null: false
end
add_index "users", ["email"], unique: true
class User < ActiveRecord::Base
end
User.create!(email: '[email protected] /cdn-cgi/l/email-protection')
u = User.create(email: '[email protected] /cdn-cgi/l/email-protection')
u.errors[:email]
=> "has already been taken"
好处是速度、易用性和完整性——
Speed
使用这种方法,您不需要在保存时进行数据库查找来检查唯一性(当索引丢失时,这有时会非常慢 -https://rails.lighthouseapp.com/projects/8994/tickets/2503-validate.. https://rails.lighthouseapp.com/projects/8994/tickets/2503-validate..。 )。如果您真的关心验证唯一性,则无论如何都必须使用数据库约束,因此数据库无论如何都会验证唯一性,并且这种方法消除了额外的查询。检查索引两次对于数据库来说不是问题(它在第二次被缓存),但是从应用程序中保存数据库往返是一个巨大的胜利。
使用方便
鉴于无论如何您都必须具有真正唯一性的数据库约束,一旦数据库约束就位,这种方法将使一切自动发生。如果您愿意,您仍然可以使用 validates_uniqueness_of 。
完整性
validates_uniqueness_of 一直有点像黑客——它无法正确处理竞争条件,并导致必须使用有些冗余的错误处理逻辑来处理异常。 (参见“并发性和完整性”部分http://api.rubyonrails.org/classes/ActiveRecord/Validations/ClassMe... http://api.rubyonrails.org/classes/ActiveRecord/Validations/ClassMe...)
验证唯一性不足以确保值的唯一性。原因是在生产中,多个工作进程可能会导致竞争条件:
-
两个并发请求尝试创建具有相同名称的用户(并且
我们希望用户名是唯一的)
-
服务器上的两个工作进程接受请求
现在将并行处理它们
-
两个请求都扫描用户表并看到名称是
可用的
-
两个请求都通过了验证并创建了一个用户
可用名称
为了更清楚地理解,请检查这个
如果您为某一列创建唯一索引,则意味着您可以保证该表不会有超过一行具有该列相同的值。在模型中仅使用 validates_uniqueness_of 验证不足以强制唯一性,因为可能有并发用户尝试创建相同的数据。
想象一下,两个用户尝试使用您在用户模型中添加了 validates_uniqueness_of :email 的同一电子邮件地址注册帐户。如果他们同时点击“注册”按钮,Rails 将在用户表中查找该电子邮件并回复一切正常,可以将记录保存到表中。然后,Rails 将使用同一电子邮件将两条记录保存到用户表中,现在您需要处理一个非常糟糕的问题。
为了避免这种情况,您还需要在数据库级别创建唯一约束:
class CreateUsers < ActiveRecord::Migration
def change
create_table :users do |t|
t.string :email
...
end
add_index :users, :email, unique: true
end
end
因此,通过创建 index_users_on_email 唯一索引,您可以获得两个非常好的好处。数据完整性和良好的性能,因为唯一索引往往非常快。
如果您在 posts 表中为 user_id 设置 unique: true ,那么它将不允许输入具有相同 user_id 的重复记录。