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?
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
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')
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]
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
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) ==================
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.