Comment rendre une colonne unique et l'indexer dans une migration Ruby on Rails?

416

Je voudrais faire une colonne uniquedans le script de migration Ruby on Rails. Quelle est la meilleure façon de procéder? Existe-t-il également un moyen d'indexer une colonne dans une table?

Je voudrais appliquer des uniquecolonnes dans une base de données au lieu de simplement utiliser :validate_uniqueness_of.

Tam
la source

Réponses:

686

La réponse courte pour les anciennes versions de Rails (voir les autres réponses pour Rails 4+):

add_index :table_name, :column_name, unique: true

Pour indexer plusieurs colonnes ensemble, vous passez un tableau de noms de colonnes au lieu d'un seul nom de colonne,

add_index :table_name, [:column_name_a, :column_name_b], unique: true

Si vous obtenez "nom d'index ... est trop long", vous pouvez ajouter name: "whatever"à la méthode add_index pour raccourcir le nom.

Pour un contrôle précis, il existe une executeméthode " " qui exécute directement SQL.

C'est ça!

Si vous faites cela en remplacement des validations régulières des anciens modèles, vérifiez comment cela fonctionne. Le rapport d'erreur à l'utilisateur ne sera probablement pas aussi agréable sans validation au niveau du modèle. Vous pouvez toujours faire les deux.

ndp
la source
36
+1 pour avoir suggéré de continuer à utiliser validates_uniqueness_of. La gestion des erreurs est beaucoup plus propre en utilisant cette méthode pour le coût d'une seule requête indexée. Je suggère qu'il fasse les deux
Steve Weet
1
J'ai essayé que ça ne semble pas marcher! Je pourrais insérer deux enregistrements avec le nom_colonne que j'ai défini comme unique! J'utilise Rails 2.3.4 et MySql des idées?
Tam
Je vous ai utilisé la deuxième suggestion en utilisant execute: execute "ALTER TABLE users ADD UNIQUE (email)" et ça marche! Je ne sais pas pourquoi le premier ne serait pas intéressé à savoir
Tam
1
Si vous obtenez une indexed columns are not uniqueerreur lorsque vous essayez de créer un index unique, cela peut être dû au fait que les données de la table contiennent déjà des doublons. Essayez de supprimer les données en double et de relancer la migration.
Hartley Brody
5
Si vous obtenez "nom d'index ... est trop long", vous pouvez ajouter , :name => "whatever"à la add_indexméthode pour raccourcir le nom.
Rick Smith du
129

les rails génèrent la migration add_index_to_table_name nom_colonne: uniq

ou

les rails génèrent la migration add_column_name_to_table_name column_name: string: uniq: index

génère

class AddIndexToModerators < ActiveRecord::Migration
  def change
    add_column :moderators, :username, :string
    add_index :moderators, :username, unique: true
  end
end

Si vous ajoutez un index à une colonne existante, supprimez ou commentez la add_columnligne ou cochez la case

add_column :moderators, :username, :string unless column_exists? :moderators, :username
d.danailov
la source
5
J'ai voté pour cela parce que je voulais le formulaire de ligne de commande. Mais c'est idiot qu'il ajoute la colonne même quand je le précise add_index...et non add_column....
Tyler Collier
1
Ouais, peut-être dans la prochaine version.
d.danailov
49

Étant donné que cela n'a pas encore été mentionné mais répond à la question que je me posais lorsque j'ai trouvé cette page, vous pouvez également spécifier qu'un index doit être unique lorsque vous l'ajoutez via t.referencesou t.belongs_to:

create_table :accounts do |t|
  t.references :user, index: { unique: true } # or t.belongs_to

  # other columns...
end

(à partir d'au moins Rails 4.2.7)

Steve Grossi
la source
42

Si vous créez un nouveau tableau, vous pouvez utiliser le raccourci en ligne:

  def change
    create_table :posts do |t|
      t.string :title, null: false, index: { unique: true }
      t.timestamps
    end
  end
Pioz
la source
14

J'utilise Rails 5 et les réponses ci-dessus fonctionnent très bien; voici une autre façon qui a également fonctionné pour moi (le nom de la table est :peopleet le nom de la colonne est :email_address)

class AddIndexToEmailAddress < ActiveRecord::Migration[5.0]
  def change
    change_table :people do |t|
      t.index :email_address, unique: true
    end
  end
end
Nicholas Nelson
la source
1

Vous souhaiterez peut-être ajouter le nom de la clé unique car le nombre de fois que le nom unique_key par rails peut être trop long pour lequel la base de données peut renvoyer l'erreur.

Pour ajouter un nom à votre index, utilisez simplement l' name:option. La requête de migration pourrait ressembler à ceci -

add_index :table_name, [:column_name_a, :column_name_b, ... :column_name_n], unique: true, name: 'my_custom_index_name'

Plus d'informations - http://apidock.com/rails/ActiveRecord/ConnectionAdapters/SchemaStatements/add_index

Échanges
la source
1
add_index :table_name, :column_name, unique: true

Pour indexer plusieurs colonnes ensemble, vous passez un tableau de noms de colonnes au lieu d'un seul nom de colonne.

murali
la source
0

Si vous avez manqué d'ajouter unique à la colonne DB, ajoutez simplement cette validation dans le modèle pour vérifier si le champ est unique:

class Person < ActiveRecord::Base
  validates_uniqueness_of :user_name
end

reportez-vous ici ci- dessus à des fins de test uniquement, veuillez ajouter un index en modifiant la colonne DB comme suggéré par @Nate

veuillez le consulter avec index pour plus d'informations

Ravistm
la source
2
Je ne recommanderais pas simplement d'ajouter la validation sans un index correspondant. La meilleure option consiste à nettoyer tous les doublons existants, puis à ajouter l'index. Sinon, vous risquez d'invalider les données existantes (ce qui entraînera l'échec de toute mise à jour de ces lignes), et vous pourriez toujours vous retrouver avec des doublons si vous avez un code qui ignore les validations Rails. (par exemple, lors de l'exécution d'un update_all ou d'insertions SQL directes)
Nate