2009-05-11 7 views
32

Estoy tratando de convertir una columna en mi aplicación Rails, por razones de argumento, pretendamos que estoy tratando de cambiar la columna age en mi tabla users por una representación de cadena en lugar de una int.¿Se pueden usar las migraciones de Can Rails para convertir datos?

En mi migración tengo esto;

def.self up 
    add_column :users, :age_text, :string 

    users = User.find(:all) 

    users.each do |u| 
     u.age_text = convert_to_text(u.age) 
     u.save 
    end 
end 

def self.convert_to_text(number) 
    #code here to convert 1 to 'one' etc 
end 

Pero parece que no funciona, ¿eso es lo que intento aquí incluso con las migraciones?

Respuesta

56

Lo que estás tratando de hacer es posible, y yo diría que es lo correcto.

Sin embargo, necesita volver a cargar la información de columna para las clases de modelo que está actualizando en la migración, para que Rails conozca las nuevas columnas. Prueba esto:

def.self up 
    add_column :users, :age_text, :string 

    User.reset_column_information 

    users = User.find(:all) 

    users.each do |u| 
     u.age_text = convert_to_text(u.age) 
     u.save 
    end 
end 

En una nota aparte, tenga en cuenta que si la tabla es grande, haciendo cambios uno por uno a echar un muuuucho tiempo .. tener cuidado con eso.

+2

lo que sería una manera mejor? – Kirschstein

+0

Depende de lo que intenta hacer ... Si es una actualización simple que debe realizarse, simplemente puede ejecutar un comando SQL con el método de ejecución (ejecutar 'actualizar conjunto de usuarios colA = 1'). De nuevo, solo ten cuidado; si tiene 10k usuarios en la tabla Usuarios, pasar por cada uno de ellos con la migración anterior probablemente tomará mucho tiempo. –

+1

A veces necesitará esto también User.connection.schema_cache.clear! – hrdwdmrbl

38

Como soy nuevo aquí no puedo comentar lo anterior, así que agregaré mi propia respuesta.

GENERALMENTE manipular datos en migraciones es una MALA idea. Las migraciones con acceso directo al modelo se pueden estancar si cambia la lógica del modelo.

Imagine en su segunda migración que ha agregado una nueva columna. Desea sembrar esa columna con nuevos datos.

Digamos también que unas semanas más tarde agrega una nueva validación al modelo, una validación que opera en un campo que aún no existe en su segunda migración. si alguna vez construyes la base de datos desde la migración 0, tendrás algunos problemas.

Sugiero usar migraciones para alterar columnas y otros medios para administrar los datos de la base de datos, especialmente al pasar a producción.

+0

Hmm, muy buen punto. Teniendo esto en cuenta, ¿cómo gestionas la conversión de datos? – Kirschstein

+1

Hago una tarea de rake. Es más fácil, y en realidad más fácil de probar también, incluso si lo arrojo después de usarlo. –

+3

¿Hay alguna forma de garantizar que las tareas de rake se ejecuten en la secuencia correcta, junto con las migraciones? ¿Qué sucede si una migración realizada después de esta cae de la columna obsoleta antes de ejecutar la tarea de rake? – Kirschstein

0

Yo diría que si puede "deshacer" los datos importados al revertir la versión de migración, entonces es apropiado colocar las importaciones en la migración.

Por ejemplo, tengo una migración que configura una gran cantidad de tablas de búsqueda y otros metadatos. Los datos para estas tablas se completan durante esta fase. A medida que cambian los datos de estas tablas de búsqueda, creo nuevos archivos YAML que almacenan los metadatos y los cargo en las migraciones posteriores (y quitamos los YAMLS, volviendo a cargar el archivo YAML anterior al retroceder una versión de migración). Esto es bastante limpio. Tengo archivos (en diferentes carpetas bien definidos en mi caso) con estos archivos:

002_setup_meta_data.rb 
002_meta_data.yaml 


007_change_meta_data.rb 
007_meta_data.yaml 

Si va a importar los datos de "producción" de otro sistema en tablas transaccionales (no estáticos), entonces yo diría usar migraciones no es apropiado. Luego, seguiría el consejo de Brian Hogan sobre el uso de tareas de rake.

4

Aquí hay un ejemplo de migración que ejecuté para convertir datos. Puede convertirlo fácilmente para usar enteros en lugar de cadenas. Hacer la conversión en SQL es mucho más rápido que cargar cada fila en Rails.

class ConvertCommentTextToText < ActiveRecord::Migration 
    def up 
    add_column :comments, :text_tmp, :text 
    # copy text column 
    execute <<-SQL 
     update comments set text_tmp = text 
    SQL 
    remove_column :comments, :text 
    rename_column :comments, :text_tmp, :text 
    end 

    def down 
    add_column :comments, :text_tmp, :string 
    # copy text column 
    execute <<-SQL 
     update comments set text_tmp = text 
    SQL 
    remove_column :comments, :text 
    rename_column :comments, :text_tmp, :text 
    end 

end 

Y para probarlo:

rake db:migrate 
rake db:rollback 
rake db:migrate 
Cuestiones relacionadas