Comment écrire une migration pour renommer un modèle ActiveRecord et sa table dans Rails?

408

Je suis horrible à nommer et je me rends compte qu'il existe un meilleur ensemble de noms pour mes modèles dans mon application Rails.
Existe-t-il un moyen d'utiliser une migration pour renommer un modèle et sa table correspondante?

Lecture seulement
la source
11
J'ai suggéré d'ajouter "ActiveRecord" à cette question pour améliorer les correspondances des moteurs de recherche. Je cherchais cela en utilisant "ActiveRecord rename table".
Landon Kuhn
6
Si vous utilisez des migrations, ce problème est plus compliqué qu'il n'y paraît. La solution sélectionnée indique de revenir en arrière et de renommer manuellement le modèle, le contrôleur, etc. après avoir modifié le nom de la table. Si vous procédez ainsi, toutes les anciennes migrations faisant référence à votre modèle par son ancien nom échoueront. Ainsi, lorsque quelqu'un clone votre référentiel et tente de s'exécuter rake db:migrate, il échoue. Vous pouvez revenir en arrière et modifier ces noms dans la migration, mais cela deviendra compliqué. Vous feriez mieux de créer un modèle entièrement nouveau plutôt que de le renommer.
andrew
4
@andrewhannigan: Votre point n'est-il pas théorique si quelqu'un clone votre repo et court rake db:schema:load?
istrasci
3
@istrasci: absolument. En fait, courir rake db:migratepour créer une base de données à partir de zéro est activement découragé, précisément en raison des préoccupations signalées par andrew.
Giuseppe

Réponses:

584

Voici un exemple:

class RenameOldTableToNewTable < ActiveRecord::Migration
  def self.up
    rename_table :old_table_name, :new_table_name
  end

  def self.down
    rename_table :new_table_name, :old_table_name
  end
end

J'ai dû aller renommer le fichier de déclaration de modèle manuellement.

Éditer:

Dans Rails 3.1 & 4, ActiveRecord::Migration::CommandRecordersait comment inverser les migrations rename_table, vous pouvez donc faire ceci:

class RenameOldTableToNewTable < ActiveRecord::Migration
  def change
    rename_table :old_table_name, :new_table_name
  end 
end

(Vous devez encore parcourir et renommer manuellement vos fichiers.)

Lecture seulement
la source
6
@mathee: oui, vous devez changer cela manuellement, ou utiliser un IDE qui peut refactoriser Ruby et le valider dans votre système de contrôle de version.
pupeno
13
git grep est votre ami. Je renomme une activité en habitude en ce moment: git grep -i activitc'est très révélateur.
Felix Rabe
1
vous devez également changer le contenu de votre contrôleur, non?
alemur
5
Et n'oubliez pas vos routes.rb!
Dan Herman
26
De plus, juste pour vous prévenir, vous souhaitez utiliser la version plurielle de votre nom de table dans l'appel rename_table.
Han
66

Dans Rails 4, tout ce que j'avais à faire était le changement de définition

def change
  rename_table :old_table_name, :new_table_name
end

Et tous mes index ont été pris en charge pour moi. Je n'avais pas besoin de mettre à jour manuellement les index en supprimant les anciens et en en ajoutant de nouveaux.

Et cela fonctionne en utilisant le changement pour monter ou descendre en ce qui concerne les index également.

bfcoder
la source
47

Les autres réponses et commentaires couvraient le changement de nom de table, le changement de nom de fichier et la lecture de votre code.

Je voudrais ajouter quelques mises en garde supplémentaires:

Prenons un exemple concret auquel j'ai été confronté aujourd'hui: renommer un modèle de «Marchand» en «Entreprise».

  • N'oubliez pas de modifier les noms des tables et modèles dépendants dans la même migration. J'ai changé mes modèles Merchant et MerchantStat en Business et BusinessStat en même temps. Sinon, j'aurais dû faire beaucoup trop de choix et de choix lors de la recherche et du remplacement.
  • Pour tous les autres modèles qui dépendent de votre modèle via des clés étrangères, les noms de colonne de clé étrangère des autres tables seront dérivés de votre nom de modèle d'origine. Vous souhaiterez donc également effectuer des appels rename_column sur ces modèles dépendants. Par exemple, j'ai dû renommer la colonne 'merchant_id' en 'business_id' dans diverses tables de jointure (pour la relation has_and_belongs_to_many) et d'autres tables dépendantes (pour les relations normales has_one et has_many). Sinon, j'aurais fini avec des colonnes comme «business_stat.merchant_id» pointant vers «business.id». Voici une bonne réponse sur la façon de renommer les colonnes.
  • Lorsque vous saluez, n'oubliez pas de rechercher les versions singulières, plurielles, en majuscules, en minuscules et même en MAJUSCULES (qui peuvent apparaître dans les commentaires) de vos chaînes.
  • Il est préférable de rechercher d'abord les versions plurielles, puis singulières. De cette façon, si vous avez un pluriel irrégulier - comme dans mon exemple marchands :: entreprises - vous pouvez obtenir tous les pluriels irréguliers corrects. Sinon, vous pouvez vous retrouver avec, par exemple, des «entreprises» (3 s) comme état intermédiaire, ce qui entraîne encore plus de recherche et de remplacement.
  • Ne remplacez pas aveuglément chaque occurrence. Si les noms de vos modèles entrent en collision avec des termes de programmation courants, avec des valeurs dans d'autres modèles ou avec du contenu textuel dans vos vues, vous risquez de devenir trop impatient. Dans mon exemple, je voulais changer le nom de mon modèle en «Business», mais toujours les désigner comme «marchands» dans le contenu de mon interface utilisateur. J'avais également un rôle de «marchand» pour mes utilisateurs dans CanCan - c'est la confusion entre le rôle de marchand et le modèle de marchand qui m'a amené à renommer le modèle en premier lieu.
armchairdj
la source
26

Vous devez également remplacer vos index:

class RenameOldTableToNewTable< ActiveRecord:Migration
  def self.up
    remove_index :old_table_name, :column_name
    rename_table :old_table_name, :new_table_name
    add_index :new_table_name, :column_name
  end 

  def self.down
    remove_index :new_table_name, :column_name
    rename_table :new_table_name, :old_table_name
    add_index :old_table_name, :column_name
  end
end

Et renommez vos fichiers, etc., manuellement comme d'autres réponses ici le décrivent.

Voir: http://api.rubyonrails.org/classes/ActiveRecord/Migration.html

Assurez-vous que vous pouvez annuler et restaurer après avoir écrit cette migration. Cela peut devenir délicat si vous vous trompez et que vous êtes bloqué par une migration qui essaie d'effectuer quelque chose qui n'existe plus. Il vaut mieux jeter toute la base de données et recommencer si vous ne pouvez pas revenir en arrière. Soyez donc conscient que vous devrez peut-être sauvegarder quelque chose.

Aussi: vérifiez schema_db pour tout nom de colonne pertinent dans d'autres tables définies par un has_ ​​ou appartient_to ou quelque chose. Vous devrez probablement également les modifier.

Et enfin, faire cela sans une suite de tests de régression serait fou.

Rimian
la source
11
Quant aux migrations de rails 4.0.0.beta1, il n'est pas nécessaire de mettre à jour les index manuellement. AR le met à jour par lui-même.
freemanoid
1

Vous pouvez exécuter cette commande: rails g migration rename_ {old_table_name} to {new_table_name}

après avoir modifié le fichier et ajouté ce code dans le changement de méthode

rename_table: {old_table_name},: {new_table_name}

Mouhamadou Bamba Mboup
la source