Migración de rieles para columna de cambio

327

Tenemos script/generate migration add_fieldname_to_tablename fieldname:datatypesintaxis para agregar nuevas columnas a un modelo.

En la misma línea, ¿tenemos un script / generar para cambiar el tipo de datos de una columna? ¿O debería escribir SQL directamente en mi migración vainilla?

Quiero cambiar una columna de datetimea date.

papdel
fuente

Respuestas:

549

Creo que esto debería funcionar.

change_column :table_name, :column_name, :date
Alex Korban
fuente
13
@b_ayan: que yo sepa, las únicas palabras mágicas en los nombres de migración son "agregar" y "eliminar".
Alex Korban
1
Una especie de carriles novatos aquí, pero ... Entiendo la respuesta, pero no los comentarios sobre esta respuesta. Aclaraciones apreciadas :)
Alan H.
77
Cuando crea una migración, le asigna un nombre (por ejemplo, add_fieldname_to_tablename en la pregunta anterior). Si comienza con "agregar" o "eliminar", la migración se completará automáticamente con código para agregar o eliminar columnas, lo que le ahorra escribir ese código usted mismo.
Alex Korban
66
También vale la pena tener en cuenta que debe reemplazar la changeacción habitual con acciones separadas upy downacciones, ya que change_columnes una migración irreversible y generará un error si necesita retroceder.
DaveStephens
1
@QPaysTaxes up debe contener lo que desea cambiar su columna desde y hacia, y down debe contener cómo revertir ese cambio.
DaveStephens
98

También puede usar un bloque si tiene varias columnas para cambiar dentro de una tabla.

Ejemplo:

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

Consulte la documentación de la API en la clase Tabla para obtener más detalles.

Juan
fuente
88

No sé si puede crear una migración desde la línea de comandos para hacer todo esto, pero puede crear una nueva migración y luego editar la migración para realizar estas tareas.

Si tablename es el nombre de su tabla, fieldname es el nombre de su campo y desea cambiar de una fecha a otra, puede escribir una migración para hacerlo.

Puede crear una nueva migración con:

rails g migration change_data_type_for_fieldname

Luego edite la migración para usar 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

Luego ejecute la migración:

rake db:migrate
Ryan
fuente
32

Como descubrí en las respuestas anteriores, se necesitan tres pasos para cambiar el tipo de columna:

Paso 1:

Genere un nuevo archivo de migración con este código:

rails g migration sample_name_change_column_type

Paso 2:

Vaya a la /db/migratecarpeta y edite el archivo de migración que realizó. Hay dos soluciones diferentes.

  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

Paso 3:

No olvides hacer este comando:

rake db:migrate

He probado esta solución para Rails 4 y funciona bien.

Aboozar Rajabi
fuente
1
En el paso 2, el primero fallará después de ejecutar rake db: rollback, le recomiendo que verifique el segundo
Feuda
¿Existe una convención de rieles que permita que todo se haga más o menos al generar el archivo de migración sin ir a él y luego editarlo?
BKSpurgeon
@BKSpurgeon Sí, consulte la documentación aquí: edgeguides.rubyonrails.org/active_record_migrations.html
Aboozar Rajabi
12

Con rieles 5

De guías de rieles :

Si desea que una migración haga algo que Active Record no sabe cómo revertir, puede usar 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
Señor tao
fuente
8

Solo genera migración:

rails g migration change_column_to_new_from_table_name

Actualice la migración de esta manera:

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

y finalmente

rake db:migrate
Vivek Sharma
fuente
2

Otra forma de cambiar el tipo de datos mediante la migración.

Paso 1: debe eliminar el nombre del campo del tipo de datos defectuoso mediante la migración

ex:

rails g migration RemoveFieldNameFromTableName field_name:data_type

Aquí no olvide especificar el tipo de datos para su campo

Paso 2: ahora puede agregar un campo con el tipo de datos correcto

ex:

rails g migration AddFieldNameToTableName field_name:data_type

Eso es todo, ahora su tabla se agregará con el campo de tipo de datos correcto, ¡Feliz codificación de rubíes!

prasanthrubyist
fuente
2

Todo esto supone que el tipo de datos de la columna tiene una conversión implícita para cualquier dato existente. Me he encontrado con varias situaciones en las que los datos existentes, digamos que a, Stringse pueden convertir implícitamente en el nuevo tipo de datos, digamos Date.

En esta situación, es útil saber que puede crear migraciones con conversiones de datos. Personalmente, me gusta ponerlos en mi archivo modelo y luego eliminarlos después de que todos los esquemas de la base de datos se hayan migrado y sean estables.

/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
fuente
0

Para completar las respuestas en caso de editar el valor predeterminado :

En la consola de tus rieles:

rails g migration MigrationName

En la migración:

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

Se vera como :

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