1 de julio de 2011

Asociación uno a muchos con ActiveRecord

Voy a continuar con mi proceso de aprendizaje de Ruby on Rails, y para ello nada mejor que ir contando las cosas a medida que se aprenden.

Hoy hablaré de las diferentes formas de mapear un modelo relacional utilizando ActiveRecord.
Para empezar vamos a ver la relación de pertenencia "uno a muchos" y para ello crearemos dos tablas que se llamarán "grupo" y "persona" y las relacionaremos. Vamos a utilizar el scaffolding para este paso:

$ rails generate model grupo nombre:string
$ rails generate model persona nombre:string

Esto crea los archivos de migración siguientes en la carpeta "db/migrate":




Y también crea los modelos vacíos en "app/models"



Ahora nos falta aplicar esas migraciones para que podamos usar los modelos:

rake db:migrate

Si ahora vamos a la consola de rails veremos que podemos crear y salvar objetos de nuestros modelos:

$ rails c
ruby-1.9.2-p180 :005 > ramones = Grupo.new(:nombre => "Los Ramones")
 => # 
ruby-1.9.2-p180 :006 > ramones.save
  SQL (64.9ms)  INSERT INTO "grupos" ("created_at", "nombre", "updated_at") VALUES (?, ?, ?)  [["created_at", Fri, 01 Jul 2011 07:57:20 UTC +00:00], ["nombre", "Los Ramones"], ["updated_at", Fri, 01 Jul 2011 07:57:20 UTC +00:00]]
 => true 
ruby-1.9.2-p180 :007 > joey = Persona.new(:nombre => "Joey Ramone")
 => # 
ruby-1.9.2-p180 :008 > joey.save
  SQL (0.4ms)  INSERT INTO "personas" ("created_at", "nombre", "updated_at") VALUES (?, ?, ?)  [["created_at", Fri, 01 Jul 2011 07:58:06 UTC +00:00], ["nombre", "Joey Ramone"], ["updated_at", Fri, 01 Jul 2011 07:58:06 UTC +00:00]]
 => true

También podemos consultar las tablas en la base de datos directamente:

$ rails db
sqlite> .tables
grupos             personas           schema_migrations
sqlite> .schema grupos
CREATE TABLE "grupos" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "nombre" varchar(255), "created_at" datetime, "updated_at" datetime);
sqlite> .schema personas
CREATE TABLE "personas" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "nombre" varchar(255), "created_at" datetime, "updated_at" datetime);
sqlite> SELECT * from grupos;
1|Los Ramones|2011-07-01 07:57:20.559158|2011-07-01 07:57:20.559158
sqlite> SELECT * from personas;
1|Joey Ramone|2011-07-01 07:58:06.926128|2011-07-01 07:58:06.926128

Pero si ahora queremos añadir una persona a un grupo no podemos. Para solucionarlo vamos a crear una relación ""uno a muchos" entre los modelos utilizando los modificadores :has_many y :belongs_to. La única diferencia entre :has_many y :belongs_to es que :belongs_to se ha de usar en el modelo que contenga la clave foránea. Nuestras modelos quedarían así:



Ahora tendríamos que hacer una migración para añadir la clave foránea grupo_id en la tabla Persona:

rails generate migration AddGrupoIdToPersona grupo_id:integer

lo que me generaría la siguiente migración, a la que le hemos añadido manualmente el indice:



luego volvemos a hacer un "rake db:migrate". Para ver si se han aplicado los cambios podemos hacerlo también mirando el archivo "db/schema.rb":



Vemos que el schema está como toca y probamos a ver si está también en la base de datos real:

$ rails db
sqlite> .schema personas
CREATE TABLE "personas" ("id" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, "nombre" varchar(255), "created_at" datetime, "updated_at" datetime, "grupo_id" integer);
CREATE INDEX "index_personas_on_grupo_id" ON "personas" ("grupo_id");

Parece todo correcto. Vamos a añadir algunas filas a las tablas, pero esta vez utilizando el fichero seeds.rb:



A continuación recreamos la base de datos vacía y vamos a poblar la base de datos:

rake db:schema:load
rake db:seed

Y ahora vamos a consultar las filas con ActiveRecord:

$ rails c
> Persona.find_by_nombre("John Lennon").grupo.nombre
  Persona Load (0.2ms)  SELECT "personas".* FROM "personas" WHERE "personas"."nombre" = 'John Lennon' LIMIT 1
  Grupo Load (0.1ms)  SELECT "grupos".* FROM "grupos" WHERE "grupos"."id" = 2 LIMIT 1
 => "The Beatles"

Si ahora queremos que John Lennon pertenezca a "Los Ramones" podríamos hacer:



Y eso es todo por hoy! En próximos capítulos intentaré ver el resto de tipos de asociaciones que podemos realizar con ActiveRecord. Hasta luego!