Migraciones y CSV con FasterCSV

En nuestras aplicaciones a veces es necesario cargar en la base de datos gran cantidad de registros que deben estar ahí siempre.

Un buen ejemplo de esto podría ser una lista de países con sus respectivas provincias y ciudades, algo con lo que seguro os encontraréis algún día si no lo habéis hecho ya.

Normalmente, antes de conocer Rails y su filosofía, si me hubiese encontrado esto lo habría importado a la base de datos de forma manual.

Ante esto, he visto que algunos optan por cargar todos esos datos en la misma migración para que esta se ejecute cuando sea necesaria, pero para ello deben jugar con la consola hasta conseguir la cadena de texto con los datos que les interesa y la pegan en la migración para posteriormente procesarlas con por ejemplo un Model.create(:mi => “hash”).

Si necesitas volcar en la DB 1000 registros el resultado del archivo que se encargue de la migración puede ser enorme, no quiero ni imaginarme una maquina modestilla intentando abrir un fichero con miles de registros y encima queriéndolos colorear…

Con las cantidades de registros que estoy andando ahora para mi MacBook no es ningún problema (es la maquina más potente de la oficina :P), pero el sistema anterior me parece feo y si los datos cambian durante el desarrollo debes re-escribir la migración. Para este tipo de cosas creo que lo ideal es tener todos esos datos en un archivo aparte y leerlos poco a poco desde la migración, así evitamos que nuestra computadora lo pase mal tanto al abrir el archivo para editarlo como para cargarlo en la base de datos.

En esta ocasión los archivos los tenía en CSV así que decidí seguir con ese mismo formato, busqué un poco y gracias a dotanything encontré FastCSV, una estupenda gema que nos facilitará el trabajo en general con archivos CSV, desde leerlos, procesarlos y hasta crearlos.

En la web de dotanything este ejemplo:

  1. FasterCSV.foreach("#{RAILS_ROOT}/lib/symbols_database/security_list.csv", :row_sep => "\r") do |row|
  2.   field1,field2,field3 = row
  3.   Foo.create(:field1 => field1, :field2 => field2, :field3 => field3)
  4. end

Sin embargo el método foreach que FastCSV ofrece no conseguí hacerlo funcionar, sin embargo se puede hacer algo similar con el metodo read y un bucle de toda la vida en ruby:

  1. FasterCSV.read("#{RAILS_ROOT}/lib/symbols_database/security_list.csv").each do |row|
  2.   field1,field2,field3 = row
  3.   Foo.create(:field1 => field1, :field2 => field2, :field3 => field3)
  4. end

Y creo que con esto tengo poco más que decir, para más información pasaros por la documentación de FasterCSV, para descargarlo podéis hacerlo desde RubyForge.

No se si es el mejor sistema o si es el más rápido pero a mi me parece mucho más cómodo y limpio.

2 comentarios ↓

#1 javier ramirez on 10.10.08 at 3:31 pm

yo hago algo parecido, parseando ficheros CSV y creando una tarea rake que me permita llamarla para inicializar en diferentes entornos.

además, me gusta usar un find_or_create_by (o un find_or_initialize_by + un bloque con los campos). Así puedo ejecutar la tarea de inicialización varias veces sin obtener duplicados.

Como en Rails el id normalmente es autoincremental, si haces un create y ejecutas dos veces, tienes el set de datos duplicado. En mi caso hago algo como esto

language = Language.find_or_initialize_by_code(fields[0])
language.english_name=fields[3]
language.save!

Y así puedo ejecutar n veces. Simplemente cada vez que ejecute me reseteará los datos de cada idioma para el código correspondiente, pero no me duplica nada.

Saludos,

#2 Ceritium on 10.13.08 at 4:25 am

Gracias por tu comentario, la verdad es que me has dado una pequeña idea con lo de la tarea Rake.

Vamos mi idea es desde la migración cargar unos mínimos datos para poder seguir desarrollando y testando sin problemas y luego una tarea rake que cargue tooooodos los datos necesarios.

Leave a Comment