¿Cómo puedo hacer que una columna sea única e indexarla en una migración de Ruby on Rails?

416

Me gustaría hacer una columna uniqueen el script de migración de Ruby on Rails. ¿Cuál es la mejor manera de hacerlo? ¿También hay una manera de indexar una columna en una tabla?

Me gustaría imponer uniquecolumnas en una base de datos en lugar de solo usarlas :validate_uniqueness_of.

Tam
fuente

Respuestas:

686

La respuesta corta para versiones antiguas de Rails (ver otras respuestas para Rails 4+):

add_index :table_name, :column_name, unique: true

Para indexar varias columnas juntas, pasa una matriz de nombres de columna en lugar de un solo nombre de columna,

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

Si obtiene "el nombre del índice ... es demasiado largo", puede agregarlo name: "whatever"al método add_index para acortar el nombre.

Para un control detallado, hay un " execute" método que ejecuta SQL directo.

¡Eso es!

Si está haciendo esto como un reemplazo para las validaciones regulares de modelos antiguos, verifique cómo funciona. El informe de errores al usuario probablemente no será tan bueno sin las validaciones a nivel de modelo. Siempre puedes hacer las dos cosas.

ndp
fuente
36
+1 por sugerir continuar usando validates_uniqueness_of. El manejo de errores es mucho más limpio usando este método por el costo de una sola consulta indexada. Sugeriría que él haga ambas cosas
Steve
1
¡Intenté que no parece funcionar! ¡Podría insertar dos registros con el nombre_columna que definí como único! Estoy usando Rails 2.3.4 y MySql alguna idea?
Tam
Usé su segunda sugerencia usando execute: execute "ALTER TABLE users ADD UNIQUE (email)" y ¡funciona! No estoy seguro por qué el primero no estaría interesado en saber
Tam
1
Si obtiene un indexed columns are not uniqueerror al intentar crear un índice único, puede deberse a que los datos en la tabla ya contienen duplicados. Intente eliminar los datos duplicados y vuelva a ejecutar la migración.
Hartley Brody
55
Si obtiene "nombre de índice ... es demasiado largo", puede agregar , :name => "whatever"al add_indexmétodo para acortar el nombre.
Rick Smith
129

los rieles generan migración add_index_to_table_name column_name: uniq

o

los rieles generan migración add_column_name_to_table_name column_name: string: uniq: index

genera

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

Si está agregando un índice a una columna existente, elimine o comente la add_columnlínea, o marque

add_column :moderators, :username, :string unless column_exists? :moderators, :username
d.danailov
fuente
55
Voté esto porque quería el formulario de línea de comando. Pero es una tontería que agregue la columna incluso cuando especifico add_index...y no add_column....
Tyler Collier
1
Yeap, tal vez en la próxima versión.
d.danailov
49

Como esto aún no se ha mencionado pero responde a la pregunta que tuve cuando encontré esta página, también puede especificar que un índice debe ser único al agregarlo a través de t.referenceso t.belongs_to:

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

  # other columns...
end

(a partir de al menos Rails 4.2.7)

Steve Grossi
fuente
42

Si está creando una nueva tabla, puede usar el acceso directo en línea:

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

Estoy usando Rails 5 y las respuestas anteriores funcionan muy bien; Aquí hay otra forma que también funcionó para mí (el nombre de la tabla es :peopley el nombre de la columna es :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
fuente
1

Es posible que desee agregar un nombre para la clave única, ya que el nombre predeterminado de unique_key por rieles puede ser demasiado largo para que la base de datos pueda arrojar el error.

Para agregar nombre a su índice, simplemente use la name:opción. La consulta de migración podría verse así:

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

Más información: http://apidock.com/rails/ActiveRecord/ConnectionAdapters/SchemaStatements/add_index

Swaps
fuente
1
add_index :table_name, :column_name, unique: true

Para indexar varias columnas juntas, pasa una matriz de nombres de columna en lugar de un solo nombre de columna.

murali
fuente
0

Si no agregó una columna única a la base de datos, simplemente agregue esta validación en el modelo para verificar si el campo es único:

class Person < ActiveRecord::Base
  validates_uniqueness_of :user_name
end

consulte aquí arriba solo para fines de prueba, agregue el índice cambiando la columna de base de datos como lo sugiere @Nate

consulte esto con el índice para obtener más información

Ravistm
fuente
2
No recomendaría simplemente agregar la validación sin un índice correspondiente. La mejor opción es limpiar cualquier duplicado existente y luego agregar el índice. De lo contrario, corre el riesgo de invalidar los datos existentes (lo que provocará un error en las actualizaciones de esas filas), y aún podría terminar con duplicados si tiene algún código que omita las validaciones de Rails. (por ejemplo, cuando se ejecuta un update_all o inserciones directas de SQL)
Nate