206 votes

Valider l'unicité de plusieurs colonnes

Existe-t-il un moyen par rails de valider qu'un enregistrement réel est unique et pas seulement une colonne ? Par exemple, un modèle/tableau d'amitié ne devrait pas pouvoir avoir plusieurs enregistrements identiques du type :

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

2 votes

Essayez d'utiliser "validates_uniqueness_of" dans votre modèle. si cela ne fonctionne pas, essayez de créer un index sur lequel vous pouvez créer une migration de feilds qui inclut une déclaration comme add_index :table, [:column_a, :column_b], :unique => true)

2 votes

Malheureusement validates :field_name, unique: true est sujet à des conditions de course, donc même si cela va à l'encontre de la voie ferrée, une contrainte réelle est préférable. @HarryJoy Je vais upvote une réponse décrivant la voie de contrainte.

0 votes

Pour être honnête, c'est un excellent moyen de s'assurer que cela n'arrivera JAMAIS. Les validations peuvent être contournées.

324voto

Dylan Markow Points 65796

Vous pouvez définir une validates_uniqueness_of appeler comme suit.

validates_uniqueness_of :user_id, :scope => :friend_id

84 votes

Je voulais juste ajouter que vous pouvez passer plusieurs paramètres de portée dans le cas où vous avez besoin de valider l'unicité sur plus de 2 champs. Par exemple : :scope => [:friend_id, :group_id].

27 votes

Bizarre que tu ne puisses pas dire validates_uniqueness_of [:user_id, :friend_id] . Peut-être faut-il y apporter un correctif ?

14 votes

Alexey, validates_uniqueness_of [:user_id, :friend_id] va juste faire la validation pour chacun des champs listés - et c'est un comportement documenté et attendu.

158voto

potashin Points 37585

Vous pouvez utiliser validates de valider uniqueness sur une colonne :

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

La syntaxe de la validation sur plusieurs colonnes est similaire, mais vous devez fournir un tableau de champs à la place :

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

Cependant Les approches de validation présentées ci-dessus ont une condition de course et ne peuvent pas garantir la cohérence. Prenons l'exemple suivant :

  1. Les enregistrements des tables de la base de données sont censés être uniques par n champs ;

  2. multiples ( deux ou plus ) demandes simultanées, traitées par des processus distincts, chacun ( les serveurs d'applications, les serveurs de travail en arrière-plan ou tout ce que vous utilisez. ), accéder à la base de données pour insérer le même enregistrement dans la table ;

  3. chaque processus en parallèle valide s'il existe un enregistrement avec le même n champs ;

  4. La validation pour chaque demande est passée avec succès, et chaque processus crée un enregistrement dans la table avec les mêmes données.

Pour éviter ce genre de comportement, il faut ajouter un fichier contrainte unique à la table db. Vous pouvez le définir avec add_index pour un (ou plusieurs) champ(s) en exécutant la migration suivante :

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

Caveat : même après avoir défini une contrainte unique, deux ou plusieurs requêtes simultanées essaieront d'écrire les mêmes données dans la base de données, mais au lieu de créer des enregistrements en double, cela entraînera l'apparition d'un message d'erreur. ActiveRecord::RecordNotUnique exception, que vous devez traiter séparément :

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

34voto

Deepak Azad Points 4547

Vous avez probablement besoin de contraintes réelles sur la base de données, comme cela a déjà été mentionné dans un commentaire, les validations souffrent de conditions de course. Pour plus de détails, voir cet article de blog - http://robots.thoughtbot.com/the-perils-of-uniqueness-validations

3voto

Tate Thurston Points 327

Cela peut être fait avec une contrainte de base de données sur les deux colonnes :

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

Vous pouvez utiliser un validateur de rails, mais en général, je recommande d'utiliser une contrainte de base de données.

Plus de lecture : https://robots.thoughtbot.com/validation-database-constraint-or-both

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X