Migrar una base de datos localizada con Ruby on Rails

Dadas las dificultades y confusiones que conlleva migrar una base de datos en Ruby on Rails, sobre todo cuando uno acaba de empezar, añadido a que la base de datos con la que trabajaba ya tenía una estructura definida en un proyecto anterior, consideré que necesitaba documentar el proceso para que se haga más fácil, tanto para mí como para quien encuentre esta entrada, al repetirlo en el futuro.

La peculiaridad de esta base de datos, una que debe ser bastante común, es que contenía campos terminados en “_es” y “_en”, correspondientes al idioma del texto que contenían. Las versiones recientes de Rails ya tienen i18n integrado, pero no el gestionar automáticamente campos en diferentes idiomas en una base de datos de manera dinámica. Después de investigar un poco pareció que el plugin Globalize2 era estable y podía encargarse del trabajo.

Por desgracia, aunque parece la mejor elección, Globalize2 crea una tabla aparte llamada “XX_translations” que contiene los nombres de los campos a traducir y el idioma, o locale, al que corresponde.

El problema de tener que lidiar con plugins cuando uno hace la migración es que Rails no hace rollbacks correctos de los errores. Por ejemplo: digamos que se nos ha olvidado algún detalle necesario para establecer las traducciones y hemos intentado crear esa tabla. La tabla se crea, pero no se registra el paso, lo que nos obliga a tener que borrar la tabla a mano y volver a migrar a esa versión cruzando los dedos para que no dé problemas.

Hay que entender bien el concepto de las versiones en Rails. Por defecto, los archivos de migración de cada tabla se crean con un nombre basado en la fecha y la hora en la que se crearon. Eso dificulta innecesariamente, o al menos me parece de momento, el proceso de migrado. Conviene renombrar esas tablas de 001 a 0XX, siendo las primeras tablas las que no tengan ninguna dependencia y por lo tanto no tengan claves foráneas. Es muy importante tenerlo en cuenta, dado que de lo contrario los índices no podrán crearse al no encontrar la tabla a la que corresponde el id de la clave foránea.

Por ejemplo, digamos que tenemos una tabla en la base de datos original llamada “ability_descriptions” que contiene cinco campos: ability, desc_high_es, desc_high_en, desc_low_es, desc_low_en. Como se ve, en realidad son tres campos, ability y desc_high y desc_low, que están traducidos a español e inglés. Migrar una tabla con campos traducidos tiene sus peculiaridades que hay que seguir a rajatabla.

class CreateAbilityDescriptions < ActiveRecord::Migration
def self.up
create_table :ability_descriptions do |t|
t.string :ability, :null => false

t.timestamps
end
AbilityDescription.create_translation_table! :desc_high => :string, :desc_low => :string
end

def self.down
drop_table :ability_descriptions
AbilityDescription.drop_translation_table!
end
end

Sólo se definen explícitamente para la migración normal los campos que no necesiten traducirse. En este caso es “ability”, de tipo string. Para que la migración cree la tabla de traducciones usamos el método “create_translation_table!” del modelo correspondiente, en este caso “AbilityDescription”. Al método hay que pasarle los campos y el tipo de dato que les corresponde. Como referencia, conviene consultar las equivalencias entre los tipos de datos de MySQL y Ruby tal como se ven en la siguiente página:

MySQL and Ruby on Rails datatypes

Es vital tener en cuenta que esta tabla no está terminada de definir para la migración. Es necesario definir en el modelo correspondiente qué campos se traducen. En este caso el archivo con el modelo estaría dentro de “/Models” y se llamaría “ability_description.rb”.


class AbilityDescription < ActiveRecord::Base
translates :desc_high, :desc_low
end

Basta en llamar a “translates” con los nombres de los campos que necesitarán traducción. Si se nos olvida este paso, la orden “rake db:migrate VERSION=XX” creará la tabla, pero fallará al no entender la orden “create_translation_table!”. Si hacemos el rollback veremos que borra la tabla anterior creada en vez de esta última, ya que no ha registrado la creación de la tabla de manera correcta y, por lo tanto, tendremos que borrarla a mano.

Hay más parámetros importantes a tener en cuenta al definir los campos. Los que he usado son “null”, “default” y “limit”. Aparte de ejemplos de este uso, el siguiente archivo de migración también tiene una clave foránea cuyo índice hay que definir.

class CreateWeapons < ActiveRecord::Migration
def self.up
create_table :weapons, :force => true do |t|
t.integer :world_object_id
t.string :type, :null => true, :limit => 20
t.string :damage, :null => true, :default => “2d6”, :limit => 4
t.integer :critical, :null => true, :default => 20
t.string :damage_type, :null => true, :default => “Ballistic”, :limit => 15
t.integer :range_increment, :null => true
t.string :size, :null => false, :default => “Med”, :limit => 15
t.integer :purchase_dc, :null => false, :default => 15
t.string :restriction_type, :null => false, :default => “Lic”, :limit => 3
t.integer :restriction_mod, :null => false, :default => 1

t.timestamps
end

#Añado las claves foráneas
add_index :weapons, :world_object_id
end

def self.down
drop_table :weapons
end
end

Con respecto a los parámetros:

  • null : la validación de si el campo se rellena o no al crear el registro no se controla en el archivo de migración. En el caso de que se intente dejar vacío un campo con null => true, lo más posible es que devuelva un error de SQL.
  • default : el valor que el campo tendrá por defecto. Entre comillas si es un string, sin comillas en caso de que sea numérico.
  • limit: sólo válido para los campos de tipo string. Define la cantidad de caracteres que el campo admitirá.

En el caso de esta tabla, la clave foránea es “world_object_id”. Rails requiere una nomenclatura concreta para las claves. Dado que la clave de cada tabla se llama id por defecto, las foráneas deben llamarse como la tabla en singular, añadiéndole al final “_id”. El índice se declara dentro de la definición del método “up” con el método “add_index”, especificando la propia tabla, en este caso “weapons”, y el campo, normalmente la clave foránea, para la que queremos crear el índice, en este caso “world_object_id”.

Es importante fijarse en la línea “create_table :weapons, :force => true do |t|”. “:force => true” no se añade por defecto, pero conviene añadirlo cuando se trabaja con plugins que interceden en la migración y cuando hay que añadir índices, ya que así ignorará advertencias que probablemente no nos interesen.

La migración en sí conviene hacerla paso a paso, comprobando que va creando las tablas, tanto las originales como las dependientes. Usaremos la siguiente orden: “rake db:migrate VERSION=XXX”. Si algo falla, nos aseguramos de dar los pasos atrás necesarios mediante “rake db:rollback”.

WikipediaWictionaryChambers (UK)Google imagesGoogle defineThe Free DictionaryJoin exampleWordNetGoogleUrban DictionaryAnswers.comrhymezone.comMerriam-Webster

2 comentarios to “Migrar una base de datos localizada con Ruby on Rails”

  1. Patsy Says:

    Hello! I know this is kind of off topic but I was
    wondering which blog platform are you using for this website?
    I’m getting sick and tired of WordPress because I’ve had
    issues with hackers and I’m looking at options for another platform. I would be fantastic if you could point me in the direction of a good platform.

  2. www.hostingphpbb.com Says:

    Post writing is also a fun, if you be acquainted with afterward you can
    write or else it is complex to write.


Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s

A %d blogueros les gusta esto: