Colonne Rails migration for change

327

Nous avons une script/generate migration add_fieldname_to_tablename fieldname:datatypesyntaxe pour ajouter de nouvelles colonnes à un modèle.

Sur la même ligne, avons-nous un script / générer pour changer le type de données d'une colonne? Ou dois-je écrire SQL directement dans ma migration vanilla?

Je veux changer une colonne de datetimeen date.

papdel
la source

Réponses:

549

Je pense que cela devrait fonctionner.

change_column :table_name, :column_name, :date
Alex Korban
la source
13
@b_ayan: pour autant que je sache, les seuls mots magiques dans les noms de migration sont "ajouter" et "supprimer".
Alex Korban
1
Sorte de rails noob ici, mais… Je comprends la réponse mais pas les commentaires sur cette réponse. Clarifications appréciées :)
Alan H.
7
Lorsque vous créez une migration, vous lui donnez un nom (par exemple, add_fieldname_to_tablename dans la question ci-dessus). Si elle commence par "ajouter" ou "supprimer", la migration sera automatiquement remplie de code pour ajouter ou supprimer des colonnes, ce qui vous évite d'écrire ce code vous-même.
Alex Korban
6
Il convient également de garder à l'esprit que vous devez remplacer l' changeaction habituelle par des actions distinctes upet down, comme change_columnc'est une migration irréversible et générera une erreur si vous devez annuler.
DaveStephens
1
@QPaysTaxes up devrait contenir ce que vous voulez changer de et vers votre colonne, et down devrait contenir comment inverser ce changement.
DaveStephens
98

Vous pouvez également utiliser un bloc si vous avez plusieurs colonnes à modifier dans une table.

Exemple:

change_table :table_name do |t|
  t.change :column_name, :column_type, {options}
end

Voir la documentation de l' API sur la classe Table pour plus de détails.

John
la source
88

Je ne sais pas si vous pouvez créer une migration à partir de la ligne de commande pour faire tout cela, mais vous pouvez créer une nouvelle migration, puis modifier la migration pour effectuer cette prise.

Si tablename est le nom de votre table, fieldname est le nom de votre champ et que vous souhaitez passer d'une date / heure à une date, vous pouvez écrire une migration pour ce faire.

Vous pouvez créer une nouvelle migration avec:

rails g migration change_data_type_for_fieldname

Modifiez ensuite la migration pour utiliser change_table:

class ChangeDataTypeForFieldname < ActiveRecord::Migration
  def self.up
    change_table :tablename do |t|
      t.change :fieldname, :date
    end
  end
  def self.down
    change_table :tablename do |t|
      t.change :fieldname, :datetime
    end
  end
end

Exécutez ensuite la migration:

rake db:migrate
Ryan
la source
32

Comme je l'ai constaté par les réponses précédentes, trois étapes sont nécessaires pour changer le type d'une colonne:

Étape 1:

Générez un nouveau fichier de migration à l'aide de ce code:

rails g migration sample_name_change_column_type

Étape 2:

Accédez au /db/migratedossier et modifiez le fichier de migration que vous avez créé. Il existe deux solutions différentes.

  1. def change
        change_column(:table_name, :column_name, :new_type)
    end

2.

    def up
        change_column :table_name, :column_name, :new_type
    end

    def down
        change_column :table_name, :column_name, :old_type
    end

Étape 3:

N'oubliez pas de faire cette commande:

rake db:migrate

J'ai testé cette solution pour Rails 4 et cela fonctionne bien.

Aboozar Rajabi
la source
1
À l'étape 2, le premier échouera après l'exécution de rake db: rollback, je vous recommande de vérifier le second
Feuda
Existe-t-il une convention rails qui permet de tout faire plus ou moins lors de la génération du fichier de migration sans y accéder, puis en le modifiant?
BKSpurgeon
@BKSpurgeon Oui, consultez la documentation ici: edgeguides.rubyonrails.org/active_record_migrations.html
Aboozar Rajabi
12

Avec Rails 5

Des guides Rails :

Si vous souhaitez qu'une migration fasse quelque chose qu'Active Record ne sait pas inverser, vous pouvez utiliser reversible:

class ChangeTablenameFieldname < ActiveRecord::Migration[5.1]
  def change
    reversible do |dir|
      change_table :tablename do |t|
        dir.up   { t.change :fieldname, :date }
        dir.down { t.change :fieldname, :datetime }
      end
    end
  end
end
M. Tao
la source
8

Générez simplement la migration:

rails g migration change_column_to_new_from_table_name

Mettre à jour la migration comme ceci:

class ClassName < ActiveRecord::Migration
  change_table :table_name do |table|
    table.change :column_name, :data_type
  end
end

et enfin

rake db:migrate
Vivek Sharma
la source
2

Une autre façon de modifier le type de données à l'aide de la migration

étape 1: vous devez supprimer le nom du champ de type de données défectueux à l'aide de la migration

ex:

rails g migration RemoveFieldNameFromTableName field_name:data_type

Ici, n'oubliez pas de spécifier le type de données pour votre champ

Étape 2: Maintenant, vous pouvez ajouter un champ avec le type de données correct

ex:

rails g migration AddFieldNameToTableName field_name:data_type

Voilà, maintenant votre table sera ajoutée avec le champ de type de données correct, codage rubis heureux !!

prasanthrubyist
la source
2

Tout cela suppose que le type de données de la colonne a une conversion implicite pour toutes les données existantes. J'ai rencontré plusieurs situations où les données existantes, disons qu'un Stringpeut être implicitement converti en nouveau type de données, disonsDate .

Dans cette situation, il est utile de savoir que vous pouvez créer des migrations avec des conversions de données. Personnellement, j'aime les mettre dans mon fichier modèle, puis les supprimer une fois que tous les schémas de base de données ont été migrés et sont stables.

/app/models/table.rb
  ...
  def string_to_date
    update(new_date_field: date_field.to_date)
  end

  def date_to_string
    update(old_date_field: date_field.to_s)
  end
  ...
    def up
        # Add column to store converted data
        add_column :table_name, :new_date_field, :date
        # Update the all resources
        Table.all.each(&:string_to_date)
        # Remove old column
        remove_column :table_name, :date_field
        # Rename new column
        rename_column :table_name, :new_date_field, :date_field
    end

    # Reversed steps does allow for migration rollback
    def down
        add_column :table_name, :old_date_field, :string
        Table.all.each(&:date_to_string)
        remove_column :table_name, :date_field
        rename_column :table_name, :old_date_field, :date_field
    end
Sebastian Scholl
la source
0

Pour compléter les réponses en cas de modification de la valeur par défaut :

Dans votre console de rails:

rails g migration MigrationName

Dans la migration:

  def change
    change_column :tables, :field_name, :field_type, default: value
  end

Ressemblera :

  def change
    change_column :members, :approved, :boolean, default: true
  end
Gregdebrick
la source