Aqui estão as diferenças entre o índice único e o validates_uniqueness_of
Este é um patch para permitir que o ActiveRecord identifique erros gerados por db para violações de restrição exclusivas. Por exemplo, ele faz o seguinte funcionar sem declarar um 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]')
u = User.create(email: '[email protected]')
u.errors[:email]
=> "has already been taken"
Os benefícios são velocidade, facilidade de uso e completude --
Velocidade
Com essa abordagem, você não precisa fazer uma pesquisa de banco de dados para verificar a exclusividade ao salvar (o que às vezes pode ser bastante lento quando o índice é perdido -- https://rails.lighthouseapp.com/projects/8994/tickets/2503-validate.. . ). Se você realmente se preocupa em validar a exclusividade, terá que usar restrições de banco de dados de qualquer maneira, para que o banco de dados valide a exclusividade, não importa o que aconteça, e essa abordagem remove uma consulta extra. Verificar o índice duas vezes não é um problema para o banco de dados (ele é armazenado em cache na segunda vez), mas salvar uma viagem de ida e volta do banco de dados do aplicativo é uma grande vitória.
Facilidade de uso
Dado que você precisa ter restrições de banco de dados para a verdadeira exclusividade de qualquer maneira, essa abordagem permitirá que tudo aconteça automaticamente quando as restrições de banco de dados estiverem em vigor. Você ainda pode usar validates_uniqueness_of se quiser.
Integridade
validates_uniqueness_of sempre foi um pouco um hack -- ele não pode lidar com condições de corrida corretamente e resulta em exceções que devem ser tratadas usando uma lógica de manipulação de erros um tanto redundante. (Consulte a seção "Simultaneidade e integridade" em http://api.rubyonrails .org/classes/ActiveRecord/Validations/ClassMe... )
validates_uniqueness_of não é suficiente para garantir a exclusividade de um valor. A razão para isso é que, na produção, vários processos de trabalho podem causar condições de corrida:
-
Duas solicitações simultâneas tentam criar um usuário com o mesmo nome (e queremos que os nomes de usuário sejam exclusivos)
-
As solicitações são aceitas no servidor por dois processos de trabalho que agora as processarão em paralelo
-
Ambas as solicitações verificam a tabela de usuários e verificam se o nome está disponível
-
Ambas as solicitações passam na validação e criam um usuário com o nome aparentemente disponível
Para uma compreensão mais clara, verifique isto
Se você criar um índice exclusivo para uma coluna, significa que você tem a garantia de que a tabela não terá mais de uma linha com o mesmo valor para essa coluna. Usar apenas validação validates_uniqueness_of em seu modelo não é suficiente para impor exclusividade porque pode haver usuários simultâneos tentando criar os mesmos dados.
Imagine que dois usuários tentem registrar uma conta com o mesmo email onde você adicionou validates_uniqueness_of :email em seu modelo de usuário. Se eles clicarem no botão “Cadastre-se” ao mesmo tempo, o Rails procurará na tabela de usuários por esse e-mail e responderá que está tudo bem e que não há problema em salvar o registro na tabela. O Rails irá então salvar os dois registros na tabela de usuário com o mesmo email e agora você tem um problema muito ruim para lidar.
Para evitar isso, você também precisa criar uma restrição exclusiva no nível do banco de dados:
class CreateUsers < ActiveRecord::Migration
def change
create_table :users do |t|
t.string :email
...
end
add_index :users, :email, unique: true
end
end
Então, ao criar o índice exclusivo index_users_on_email, você obtém dois benefícios muito bons. Integridade de dados e bom desempenho porque índices exclusivos tendem a ser muito rápidos.
Se você colocar unique:true em sua tabela de posts para user_id, não permitirá inserir registros duplicados com o mesmo user_id.