Sembrando una Base de Datos en Ruby on Rails

Ruby on Rails tiene herramientas excelentes para sembrar una base de datos (rellenarla con datos), y gracias al trabajo de la comunidad, muchas gemas hacen esta tarea más fácil.
Aparte de sembrar una base de datos, tenemos herramientas útiles para comprobar la base de datos y organizar mejor las semillas de datos.
¿Quieres ver este artículo en formato video?
Creando una aplicación de ejemplo
Empecemos creando una aplicación nueva.
rails new ejemplo
cd ejemplo
Code language: Shell Session (shell)
Creando un modelo
Después, genera un modelo nuevo. Si tienes curiosidad, tecleando rails generate
(o el atajo rails g
), verás todos los generadores disponibles.
rails g model Pelicula titulo director sinopsis:text vista_en:date
Code language: Shell Session (shell)
Aquí estás definiendo titulo
y director
como cadenas (el tipo por defecto si no se especifica), sinopsis
como texto, y vista_en
como fecha (cuando se definen fechas, sin hora, la convención es añadir en
al nombre).
Rails generará una migración adaptada a la base de datos por defecto, que es SQLite. Las migraciones se guardan en db/migrate
. Veamos cómo es:
class CreatePeliculas < ActiveRecord::Migration[6.1]
def change
create_table :peliculas do |t|
t.string :titulo
t.string :director
t.text :sinopsis
t.date :vista_en
t.timestamps
end
end
end
Code language: Ruby (ruby)
Como puedes ver, Rails añade la versión que estás usando entre corchetes al final de la clase padre.
La directiva de timestamps generará los campos created_at
y updated_at
automáticamente. Muy útil.
Vamos a ejecutarlo.
$ rails db:migrate
== 20210311174407 CreatePeliculas: migrating =====================================
-- create_table(:peliculas)
-> 0.0020s
== 20210311174407 CreatePeliculas: migrated (0.0021s) ============================
Code language: Shell Session (shell)
Ahora Rails ha creado la tabla. Por si acaso te has equivocado en algo, siempre puedes volver atrás:
$ rails db:rollback
== 20210311174407 CreatePeliculas: reverting =====================================
-- drop_table(:peliculas)
-> 0.0019s
== 20210311174407 CreatePeliculas: reverted (0.0064s) ============================
Code language: Shell Session (shell)
Este comando acepta un parámetro step
opcional para volver atrás tantas migraciones como necesites. Por ejemplo, si quieres deshacer 2 migraciones, puedes hacerlo así: rails db:rollback STEP=2
Vamos a ver cómo está el esquema en db/schema.rb
después de ejecutar la migración:
ActiveRecord::Schema.define(version: 2021_03_11_174407) do
create_table "peliculas", force: :cascade do |t|
t.string "titulo"
t.string "director"
t.text "sinopsis"
t.date "vista_en"
t.datetime "created_at", precision: 6, null: false
t.datetime "updated_at", precision: 6, null: false
end
end
Code language: Ruby (ruby)
¡Bien! Este archivo contendrá el esquema entero de la base de datos a medida que vayamos haciendo migraciones.
Comandos de Rails
¿Cómo puedes saber los comandos de Rails que hay disponibles? Puedes ver una lista usando el parámetro -T
:
rails -T
Code language: Shell Session (shell)
Incluso puedes filtrar por espacio de nombre, como db
:
rails -T db
Code language: Shell Session (shell)
Creando algunas semillas
Vamos a la parte interesante de este artículo. Abre el archivo db/seeds.rb
, y pega esto:
Pelicula.destroy_all
Pelicula.create!([{
titulo: "Soul",
director: "Pete Docter",
sinopsis: "Un músico que ha perdido su pasión por la música es transportado fuera de su cuerpo y debe encontrar el camino de regreso con la ayuda de un alma infantil que aprende sobre sí misma.",
vista_en: 1.week.ago
},
{
titulo: "El Señor de los Anillos: La Comunidad del Anillo",
director: "Peter Jackson",
sinopsis: "En la adormecida e idílica Comarca, un joven hobbit recibe un encargo: custodiar el Anillo Único y emprender el viaje para su destrucción en las Grietas del Destino.",
vista_en: 2.years.ago
},
{
titulo: "Terminator 2",
director: "James Cameron",
sinopsis: "Arnold Schwarzenegger es Terminator: un exterminador modelo T-800 CSM-101 capturado y reprogramado por John Connor y enviado al pasado para proteger a Sarah Connor y a él mismo de joven.",
vista_en: 3.years.ago
}])
p "Creadas #{Pelicula.count} películas"
Code language: Ruby (ruby)
Primero, borras todas las películas para tener un estado limpio y añades tres películas pasando un array al método create
. El archivo de semillas usa ActiveSupport de Rails, para usar esos comandos X.ago
tan útiles de Ruby para definir fechas.
Al final, hay un pequeño feedback sobre el total de películas creadas. ¡Vamos a ejecutarlo!
$ rails db:seed
"Creadas 3 películas"
Code language: Shell Session (shell)
Puedes ejecutar este comando tantas veces como necesites. Se borran los registros existentes gracias a la primera línea que contiene el destroy
.
Para comprobarlas, puedes usar rails runner
:
$ rails runner 'p Pelicula.pluck :titulo'
["Soul", "El Señor de los Anillos: La Comunidad del Anillo", "Terminator 2"]
Code language: Shell Session (shell)
Usando una tarea personalizada de Rails para sembrar datos reales
Todas tus semillas son consideradas datos de desarrollo, no datos finales para uso en producción. Así que, no siembres en producción de la manera que acabas de hacer. ¡Sobretodo porque el primer paso elimina todas las películas!
Para sembrar datos reales, es mejor crear una tarea personalizada de Rails. Vamos a generar una para añadir géneros.
Primero genera el modelo y luego migra la base de datos. Finalmente crea la tarea.
rails g model Genero nombre
rails db:migrate
rails g task peliculas sembrar_generos
Code language: Shell Session (shell)
Este comando crea un archivo rake de películas en el directorio lib/tasks
conteniendo la tarea sembrar_generos
.
Copia el código de abajo y pégalo en lib/tasks/peliculas.rake
:
namespace :peliculas do
desc "Siembra géneros"
task sembrar_generos: :environment do
Genero.create!([{
nombre: "Acción"
},
{
nombre: "Ciencia Ficción"
},
{
nombre: "Aventura"
}])
p "Creados #{Genero.count} géneros"
end
end
Code language: Ruby (ruby)
Ya aparece en la lista de comandos de Rails:
$ rails -T peliculas
rake peliculas:sembrar_generos # Siembra géneros
Code language: Shell Session (shell)
¡Hora de ejecutarla!
$ rails peliculas:sembrar_generos
"Creados 3 géneros"
Code language: Shell Session (shell)
Cargar semillas usando la consola
La consola es útil para jugar con tus datos. Vamos a abrirla:
$ rails c
Loading development environment (Rails 6.1.3)
Code language: Shell Session (shell)
¿Sabías que puedes cargar y acceder a tus semillas desde dentro? Prueba esto:
Rails.application.load_seed
Code language: Ruby (ruby)
Jugando con datos usando la consola en modo sandbox
A veces necesitarás ejecutar comandos destructivos en datos reales en tu entorno de desarrollo o producción sin que los cambios sean permanentes. Es como un modo seguro donde puedes hacer lo que quieras y volver a un estado anterior.
Este modo se llama sandbox, y puedes acceder a él con el comando rails c --sandbox
Esta técnica es útil para depurar una base de datos real, como por ejemplo cuando un usuario dice que intenta actualizar su nombre de perfil y ve un error raro. Podrías reproducir ese error directamente usando el modo sandbox sin que afecte a los datos reales.
Cargando más semillas usando Faker
Si necesitas, por ejemplo, 100 películas, puedes reemplazar tu archivo app/db/seeds.rb
con esto:
Pelicula.destroy_all
100.times do |index|
Pelicula.create!(titulo: "Título #{index}",
director: "Director #{index}",
sinopsis: "Sinopsis #{index}",
vista_en: index.days.ago)
end
p "Creadas #{Pelicula.count} películas"
Code language: Ruby (ruby)
Ahora ejecuta rails db:seed
:
$ rails db:seed
"Creadas 100 películas"
Code language: Shell Session (shell)
Pero el resultado no parece nada realista:
$ rails runner 'p Pelicula.select(:titulo, :director, :sinopsis).last'
#<Pelicula id: nil, titulo: "Título 99", director: "Director 99", sinopsis: "Sinopsis 99">
Code language: Shell Session (shell)
Hora de usar Faker, una gema que genera valores aleatorios. Añádela en el grupo de desarrollo en tu Gemfile
:
group :development, :test do
# ...
gem 'faker'
end
Code language: Ruby (ruby)
Ejecuta bundle install
y reemplaza app/db/seeds.rb
con esto:
Pelicula.destroy_all
100.times do |index|
Pelicula.create!(titulo: Faker::Lorem.sentence(word_count: 3, supplemental: false, random_words_to_add: 0).chop,
director: Faker::Name.name,
sinopsis: Faker::Lorem.paragraph,
vista_en: Faker::Time.between(from: 4.months.ago, to: 1.week.ago))
end
p "Creadas #{Pelicula.count} películas"
Code language: Ruby (ruby)
Prueba otra vez:
$ rails db:seed
"Creadas 100 películas"
$ rails runner 'p Pelicula.select(:titulo, :director, :sinopsis, :vista_en).last'
#<Pelicula id: nil, titulo: "Toy Story 2", director: "Michael Dickinson", sinopsis: "Modi esse et at eum deserunt harum qui itaque reru...", vista_en: "2020-11-18">
Code language: Shell Session (shell)
¡Mucho mejor!
Conclusión
Sembrar la base de datos mientras desarrollas la aplicación es esencial, porque te dará la sensación de estar trabajando con datos reales.
También, conocer las herramientas disponibles para trabajar con semillas es más cómodo y productivo, así que vale la pena invertir tiempo en aprenderlas.