web-dev-qa-db-fra.com

Quelle est la syntaxe correcte pour remove_index dans une migration Rails 3.1.0?

Je suis en train d'ajouter Devise à une application Rails existante, avec une table Users déjà définie. Le générateur de devise a poussé la migration suivante:

class AddDeviseToUsers < ActiveRecord::Migration
  def self.up
    change_table(:users) do |t|

     ## Database authenticatable
     t.string :email,              :null => false, :default => ""
     t.string :encrypted_password, :null => false, :default => ""

     ## Recoverable
     t.string   :reset_password_token
     t.datetime :reset_password_sent_at

     ## Rememberable
     t.datetime :remember_created_at

     ## Trackable
     t.integer  :sign_in_count, :default => 0

     blah blah blah....

   end

   add_index :users, :email,                :unique => true
   add_index :users, :reset_password_token, :unique => true
 end

La migration vers le bas n'est pas générée et j'ai beaucoup de mal à supprimer ces index. Je vois différentes notations suggérées dans la documentation et différentes suggestions en ligne, mais aucune d'entre elles ne semble fonctionner pour moi. Par exemple...

def self.down
  change_table(:users) do |t|
    t.remove  :email
    t.remove  :encrypted_password

    t.remove  :reset_password_token

    blah blah blah...
  end

  remove_index :users, :email
  remove_index :users, :reset_password_token
end

résulte en...

An error has occurred, this and all later migrations canceled:

Index name 'index_users_on_email' on table 'users' does not exist

ce qui est étrange, car si je vérifie la base de données, bien sûr, 'index_users_on_email' est juste là ...

J'ai essayé d'autres variantes, notamment

remove_index :users, :column => :email

remove_index :users, 'email'

ou:

change_table(:users) do |t|
  t.remove_index :email
end

... mais pas de dés. J'exécute Rails 3.1.0, Ruby 1.9.2, rake 0.9.2.2, avec Postgres.

La commande qui me laisse tomber est:

bundle exec rake db:rollback STEP=1

après avoir appliqué avec succès la migration vers le haut. Aucun conseil?

64
doublea

Selon le type de base de données, vous n'avez pas à vous soucier de la suppression des index dans le self.down méthode puisque l'index sera automatiquement supprimé de la base de données lorsque vous supprimerez la colonne.

Vous pouvez également utiliser cette syntaxe dans votre self.down méthode:

def self.down
   remove_column :users, :email
   remove_column :users, :encrypted_password
   remove_column :users, :reset_password_token
end
44
iwasrobbed

Pour mémoire, la façon de supprimer un index par son nom est

remove_index(:table_name, :name => 'index_name')

donc dans votre cas

remove_index(:users, :name => 'index_users_on_email')
154
Dty

Vous pouvez également supprimer l'index spécifiant les colonnes, ce qui de mon point de vue est moins sujet aux erreurs que d'écrire le nom

remove_index :actions, :column => [:user_id, :action_name]
58
Maragues

Je voudrais développer la réponse de @ iWasRobbed. Si vous avez un index sur une seule colonne, vous vous inquiétez de remove_index n'a pas de sens puisque (juste une supposition!) la base de données doit être suffisamment intelligente pour nettoyer les ressources utilisées par cet index. Mais dans le cas où vous avez plusieurs colonnes d'index, la suppression de la colonne réduira l'index aux colonnes existantes, ce qui est tout à fait sensé, mais montre en quelque sorte où vous voudrez peut-être utiliser remove_index explicitement.

Juste à titre d'illustration - la migration ci-dessous a cette faille qu'après avoir été appliquée de haut en bas, elle laissera l'index unique sur email (ce qui signifie que la partie down ne fait pas son travail correctement)

class AddIndexes < ActiveRecord::Migration
  def up
    add_column :users, :action_name, :string
    add_index  :users, [:email, :action_name], unique: true
  end

  def down
    remove_column :users, :action_name
  end
end

Changer le bloc down en

  def down
    remove_index :users, [:email, :action_name]
    remove_column :users, :action_name
  end

va corriger cette faille et permettre à la migration de retourner correctement DB à l'état précédent avec rake db:rollback

5
dolzenko

Voici ma version complète de ceci (en Rails 5):

J'ai team_id comme index dans les fournisseurs de tables. Je n'ai plus besoin de cette relation. S'en débarasser. A fait ce qui suit:

1) créez la migration.

  $ Rails generate migration RemoveTeam_idFromVendor team_id:integer

2) Exécutez la migration, donnez-moi cette erreur. Et cela parce que la table fournisseur a des lignes dont la clé étrangère fait référence à la valeur de clé primaire de la table d'équipe

== 20170727202815 RemoveTeamIdFromVendor: migrating ===========================
-- remove_column(:vendors, :team_id, :integer)
rake aborted!
StandardError: An error has occurred, this and all later migrations canceled:

SQLite3::ConstraintException: FOREIGN KEY constraint failed: DROP TABLE "vendors"

3) Pour résoudre ce problème et lancer la migration, j'ai fait ce qui suit (Remarque: je suis en dev):

$ rake db:drop


Dropped database 'db/development.sqlite3'
Dropped database 'db/test.sqlite3'


$ rake db:create
Created database 'db/development.sqlite3'
Created database 'db/test.sqlite3'

$ rake db:migrate
~
~
~

== 20170727202815 RemoveTeamIdFromVendor: migrating ===========================
-- remove_column(:vendors, :team_id, :integer)
   -> 0.0185s
== 20170727202815 RemoveTeamIdFromVendor: migrated (0.0185s) ==================
0
zee

Pour modifier une table et/ou ses indéces, utilisez #change_table à l'intérieur #change action d'une migration. Ensuite, vous pourrez créer la suppression d'index réversible comme suit:

def change
   change_table :users do |t|
      t.index :email, :unique => true
      t.index :reset_password_token, :unique => true
   end
end

Lorsque vous devez supprimer une table avec son index bien sûr avec une action réversible, vous pouvez utiliser #drop_table méthode pour SchemaStatements avec #index méthode de la classe Table pour ConnectionAdapter:

def change
   drop_table :users do |t|
      t.index :email, :unique => true
      t.index :reset_password_token, :unique => true
   end
end

Dans le cas où vous auriez besoin exactement du #up/down paire dans une migration. Utilisez juste un #change_table avec #remove_index méthode de la classe Table pour ConnectionAdapter:

def up
   change_table :users do |t|
      t.index :email, :unique => true
      t.index :reset_password_token, :unique => true
   end
end

def down
   change_table :users do |t|
      t.remove_index :email, :unique => true
      t.remove_index :reset_password_token, :unique => true
   end
end

Toutes les méthodes sont disponibles dans la version Rails de 2.1.0 ou des versions antérieures.