PostgreSQL
 sql >> Base de données >  >> RDS >> PostgreSQL

Rails quelle est la différence dans l'index unique et validates_uniqueness_of

Voici la différence entre l'index unique et validates_uniqueness_of

Il s'agit d'un correctif permettant à ActiveRecord d'identifier les erreurs générées par la base de données pour les violations de contraintes uniques. Par exemple, il effectue le travail suivant sans déclarer un 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"

Les avantages sont la rapidité, la facilité d'utilisation et l'exhaustivité --

Vitesse

Avec cette approche, vous n'avez pas besoin de faire une recherche dans la base de données pour vérifier l'unicité lors de l'enregistrement (ce qui peut parfois être assez lent lorsque l'index est manqué -- https://rails.lighthouseapp.com/projects/8994/tickets/2503-validate.. . ). Si vous vous souciez vraiment de valider l'unicité, vous devrez de toute façon utiliser des contraintes de base de données afin que la base de données valide l'unicité quoi qu'il arrive et cette approche supprime une requête supplémentaire. Vérifier l'index deux fois n'est pas un problème pour la base de données (il est mis en cache la deuxième fois), mais enregistrer un aller-retour de base de données à partir de l'application est une grande victoire.

Facilité d'utilisation

Étant donné que vous devez de toute façon avoir des contraintes db pour une véritable unicité, cette approche laissera tout se passer automatiquement une fois les contraintes db en place. Vous pouvez toujours utiliser validates_uniqueness_of si vous le souhaitez.

Exhaustivité

validates_uniqueness_of a toujours été un peu un hack - il ne peut pas gérer correctement les conditions de concurrence et entraîne des exceptions qui doivent être gérées à l'aide d'une logique de gestion des erreurs quelque peu redondante. (Voir la section "Concurrence et intégrité" dans http://api.rubyonrails .org/classes/ActiveRecord/Validations/ClassMe... )

validates_uniqueness_of n'est pas suffisant pour assurer l'unicité d'une valeur. La raison en est qu'en production, plusieurs processus de travail peuvent provoquer des conditions de concurrence :

  1. Deux requêtes simultanées essaient de créer un utilisateur avec le même nom (et nous voulons que les noms d'utilisateur soient uniques)

  2. Les requêtes sont acceptées sur le serveur par deux processus de travail qui vont maintenant les traiter en parallèle

  3. Les deux requêtes analysent la table des utilisateurs et voient que le nom est disponible

  4. Les deux requêtes passent la validation et créent un utilisateur avec le nom apparemment disponible

Pour une compréhension plus claire, veuillez vérifier ceci

Si vous créez un index unique pour une colonne, cela signifie que vous êtes assuré que la table n'aura pas plus d'une ligne avec la même valeur pour cette colonne. L'utilisation uniquement de la validation validates_uniqueness_of dans votre modèle n'est pas suffisante pour appliquer l'unicité car il peut y avoir des utilisateurs simultanés essayant de créer les mêmes données.

Imaginez que deux utilisateurs essaient d'enregistrer un compte avec le même e-mail où vous avez ajouté validates_uniqueness_of :email dans votre modèle d'utilisateur. S'ils cliquent sur le bouton "S'inscrire" en même temps, Rails recherchera cet e-mail dans la table des utilisateurs et répondra que tout va bien et que vous pouvez enregistrer l'enregistrement dans la table. Rails enregistrera ensuite les deux enregistrements dans la table des utilisateurs avec le même e-mail et vous avez maintenant un problème vraiment merdique à résoudre.

Pour éviter cela, vous devez également créer une contrainte unique au niveau de la base de données :

class CreateUsers < ActiveRecord::Migration
  def change
    create_table :users do |t|
      t.string :email
      ...
    end
    
    add_index :users, :email, unique: true
  end
end

Ainsi, en créant l'index unique index_users_on_email, vous bénéficiez de deux avantages très intéressants. Intégrité des données et bonnes performances car les index uniques ont tendance à être très rapides.

Si vous mettez unique :true dans votre table de messages pour user_id, cela ne permettra pas d'entrer des enregistrements en double avec le même user_id.