¿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.

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.


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

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)


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

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

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


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.


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


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
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.