Como faço para tornar uma coluna exclusiva e indexá-la em uma migração do Ruby on Rails?

416

Gostaria de criar uma coluna uniqueno script de migração Ruby on Rails. Qual é a melhor maneira de fazer isso? Também existe uma maneira de indexar uma coluna em uma tabela?

Eu gostaria de impor unique colunas em um banco de dados, em vez de apenas usar :validate_uniqueness_of.

Tam
fonte

Respostas:

686

A resposta curta para versões antigas do Rails (veja outras respostas para o Rails 4+):

add_index :table_name, :column_name, unique: true

Para indexar várias colunas juntas, você passa uma matriz de nomes de colunas em vez de um único nome de coluna,

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

Se você receber "o nome do índice ... é muito longo", poderá adicionar name: "whatever"ao método add_index para tornar o nome mais curto.

Para controle refinado, existe um executemétodo " " que executa SQL direto.

É isso aí!

Se você estiver fazendo isso como um substituto para validações regulares de modelos antigos, verifique como ele funciona. O relatório de erros para o usuário provavelmente não será tão bom sem as validações no nível do modelo. Você sempre pode fazer as duas coisas.

ndp
fonte
36
+1 por sugerir continuar usando o validates_uniqueness_of. O tratamento de erros é muito mais limpo usando esse método pelo custo de uma única consulta indexada. Sugiro que ele faça as duas coisas
Steve Weet
1
Eu tentei que não parece funcionar! Eu poderia inserir dois registros com o nome da coluna que defini como exclusivo! Estou usando o Rails 2.3.4 e MySql alguma idéia?
Tam
Eu usei sua segunda sugestão usando execute: execute "ALTER TABLE users ADD UNIQUE (email)" e funciona! Não sei por que o primeiro não estaria interessado em saber
Tam
1
Se você receber um indexed columns are not uniqueerro ao tentar criar um índice exclusivo, talvez seja porque os dados na tabela já contêm duplicatas. Tente remover os dados duplicados e executar a migração novamente.
quer
5
Se você receber "o nome do índice ... é muito longo", poderá adicionar , :name => "whatever"ao add_indexmétodo para diminuir o nome.
Rick Smith
129

trilhos geram migração add_index_to_table_name column_name: uniq

ou

trilhos geram migração add_column_name_to_table_name column_name: string: uniq: index

gera

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

Se você estiver adicionando um índice a uma coluna existente, remova ou comente a add_columnlinha ou marque

add_column :moderators, :username, :string unless column_exists? :moderators, :username
d.danailov
fonte
5
Votei isso porque queria o formulário de linha de comando. Mas é bobagem que ela adicione a coluna mesmo quando eu especificar add_index...e não add_column....
Tyler Collier
1
Sim, talvez na próxima versão.
precisa saber é o seguinte
49

Como isso ainda não foi mencionado, mas responde à pergunta que fiz quando encontrei esta página, você também pode especificar que um índice seja único ao adicioná-lo via t.referencesou t.belongs_to:

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

  # other columns...
end

(a partir de pelo menos Rails 4.2.7)

Steve Grossi
fonte
42

Se você estiver criando uma nova tabela, poderá usar o atalho embutido:

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

Estou usando o Rails 5 e as respostas acima funcionam muito bem; aqui está outra maneira que também funcionou para mim (o nome da tabela é :peoplee o nome da coluna é :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
fonte
1

Você pode querer adicionar um nome para a chave exclusiva quantas vezes o nome padrão da chave única pelos trilhos puder ser muito longo para o qual o banco de dados possa gerar o erro.

Para adicionar um nome ao seu índice, basta usar a name:opção A consulta de migração pode ser algo como isto -

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

Mais informações - http://apidock.com/rails/ActiveRecord/ConnectionAdapters/SchemaStatements/add_index

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

Para indexar várias colunas juntas, você passa uma matriz de nomes de colunas em vez de um único nome de coluna.

murali
fonte
0

Se você deixou de adicionar uma coluna exclusiva ao banco de dados, basta adicionar esta validação no modelo para verificar se o campo é exclusivo:

class Person < ActiveRecord::Base
  validates_uniqueness_of :user_name
end

consulte aqui Acima é apenas para fins de teste, por favor, adicione o índice alterando a coluna DB, conforme sugerido por @Nate

consulte este índice para obter mais informações

Ravistm
fonte
2
Eu não recomendaria apenas adicionar a validação sem um índice correspondente. A melhor opção é limpar as duplicatas existentes e adicionar o índice. Caso contrário, você corre o risco de invalidar os dados existentes (o que causará falhas nas atualizações dessas linhas) e ainda poderá acabar com duplicatas se houver algum código que ignore as validações do Rails. (por exemplo, ao executar um update_all ou inserções diretas do SQL)
Nate