Revista Informática

Ruby on Rails desde Cero: ActiveRecord Asociaciones

Publicado el 23 agosto 2013 por Codehero @codeheroblog

Las series de cursos Ruby on Rails en CodeHero buscan otorgarte los conocimientos necesarios, para que puedas desarrollar tus propias aplicaciones Web. En el capítulo anterior estudiamos conceptos básicos de ActiveRecord, pero aún nos quedan conceptos de este tema.

En este nuevo capítulo estudiaremos las asociaciones con ActiveRecord, tema que se debe conocer bien para luego proseguir con la parte dos de ActiveRecord.


Asociaciones

¿Por qué necesitamos las asociaciones entre los modelos? Porque hacen operaciones comunes más simple y más fácil en el código. Por ejemplo, si tenemos una aplicación Rails sencilla que incluye un modelo para los usuarios y un modelo para las direcciones y cada usuario puede tener varias direcciones. Esto sin las asociaciones de Rails tendríamos que hacer consultas y validaciones por separado para cada uno de los objetos, esto aunque no esta mal, es ineficiente. Veamos un poco como es la sintaxis para estos ejemplo y así posteriormente mostrar los tipos de asociaciones de tablas que podemos hacer para simplificarnos la vida:

class Usuario < ActiveRecord::Base
  has_many :direccions, dependent: :destroy
end
 
class Direccion < ActiveRecord::Base
  belongs_to :Usuario
end

Con esta sintaxis le estamos diciendo a Rails que un usuario puede tener muchas direcciones (“has_many”) y una dirección solo puede tener un Usuario (“belongs_to”), a su vez al agregar la dependencia de (“destroy”) le estaríamos agregando una relación de cascada al eliminar usuarios, es decir, si borramos un usuario eliminaríamos todas sus direcciones de inmediato.

Tipos de asociaciones

En Rails, tenemos seis tipos de asociaciones para enlazar modelos ActiveRecord, que son los siguientes:

belongs_to

Esta asociación establece una relación de uno a uno con otro modelo, de forma que cada instancia del modelo “pertenece” a una instancia del otro modelo. Por ejemplo, si la aplicación tiene Usuarios y Direcciones y cada dirección le pertenece a un usuario. Esto se definiría así:

class Direccion < ActiveRecord::Base
  belongs_to :usuario
end

has_one

Esta asociación también establece un uno a uno con otro modelo, pero con algunas diferencias con respecto a tipo de asociación anterior. Esta asociación indica que cada instancia de un modelo contiene o posee una instancia de otro modelo. Por ejemplo, si cada Usuario tiene una Dirección, se declararía de la siguiente manera:

class Usuario < ActiveRecord::Base
  has_one :direccion
end

Llegado a este punto quizás te preguntaras, si al crear una relación uno a uno que tipo de asociación debemos elegir para cada uno de los componentes.

La diferencia básicamente es que has_one nos dice que le pertenece algún objeto y el belongs_to que pertenece a algo, tal y como vimos en el ejemplo anterior un usuario posee una dirección y la dirección le pertenece a un usuario.

has_many

Esta asociación indica el enlace de uno a muchos con otro modelo. Esta asociación pueden encontrarse por lo general en el opuesto de un “belongs_to” e indica que cada objeto de un modelo puede tener muchos objetos relacionados a el (o ninguno). Por ejemplo Un usuario puede tener múltiples direcciones eso se declararía de la siguiente manera:

class Usuario < ActiveRecord::Base
  has_many :direccions
end

has_many :through

Esta asociación se utiliza a menudo para establecer conexiones de muchos a muchos con otro modelo. Esta asociación al igual que “has_many” nos indica que cada objeto de un modelo puede tener muchos objetos asociados, la única diferencia es que para lograr un muchos a mucho debemos agregar un tercer modelo que relacione los anteriores. Entendamos mejor esto con un ejemplo: Si un cliente lo visitan a su dirección muchos vendedores y un Vendedor visita muchos clientes, esta afirmación se declararía de la siguiente manera:

class Usuario < ActiveRecord::Base
  has_many :direccions
  has_many :vendedors, through: :direccions
end
 
class Direccion < ActiveRecord::Base
  belongs_to :vendedor
  belongs_to :usuario
end
 
class Vendedor < ActiveRecord::Base
  has_many :direccions
  has_many :usuarios, through: :direccions
end

Con este ejemplo al invocar un vendedor podemos tener todas las direcciones y los clientes a visitar rápidamente.

has_one :through

Esta asociación se utiliza para realizar enlaces uno a uno y nos es útil para establecer algunos atajos a través de otra entidad, por ejemplo si un usuario tiene una dirección y a su vez esta dirección tienen un numero de teléfono, quizás sea apropiado establecer un atajo para obtener todos los teléfonos de un usuario, esta afirmación se pudiera establecer de la siguiente manera:

class Usuario < ActiveRecord::Base
  has_one :direccion
  has_one :telefono, through: :direccion
end
 
class Direccion < ActiveRecord::Base
  belongs_to :usuario
  has_one :telefono
end
 
class Telefono < ActiveRecord::Base
  belongs_to :direccion
end

has_and_belongs_to_many

Por último esta asociación crea una relación de muchos a muchos con otro modelo, sin necesidad de declarar un modelo para la entidad débil que enlaza estas dos entidades en la base de datos. Por ejemplo, si tenemos una aplicación que donde un vendedor puede atender a múltiples clientes y a su vez un cliente puede ser atendido por múltiplos vendedores (sin que nos importe la entidad que los relacionaría en la base de datos), entonces podemos diseñar esta estructura de la siguiente forma:

class Vendedor < ActiveRecord::Base
  has_and_belongs_to_many :clientes
end
 
class Cliente < ActiveRecord::Base
  has_and_belongs_to_many :vendedors
end

Al igual que con el has_one y belongs_to llegado a este punto podríamos preguntarnos: ¿Qué debemos utilizar para establecer una relación de muchos a muchos: has_and_belongs_to_many o has_many :through?

La respuesta la tenemos estudiando la necesidad del problema, si en nuestro planteamiento vemos necesario trabajar con el modelo que relaciona las dos entidades principales utilizamos has_many :through, o por el contrario, no tenemos pensado hacer ningún uso de esta entidad relación a nivel de código, nos conviene más utilizar has_and_belongs_to_many

Polimorfismo en las asociaciones

Esta es una forma de asociación un poco mas avanzada que nos permite relacionar un modelo y éste pueda pertenecer a más de un modelo en una sola asociación. El típico ejemplo para atender mejor esto, es el del objeto multimedia, por lo general utilizamos este modelo para asociarlo a otros objetos como usuarios o productos y así estos últimos puedan manejar sus imágenes independientes. Veamos como seria esto en código:

class Multimedia < ActiveRecord::Base
  belongs_to :multimediable, polymorphic: true
end
 
class Usuario < ActiveRecord::Base
  has_many :multimedias, as: :multimediable
end
 
class Producto < ActiveRecord::Base
  has_many :multimedias, as: :multimediable
end

Conclusión

En esta lección conocimos la estructura para establecer relaciones o asociaciones entre modelos, conceptos bastante útiles que nos permitirán crear y manipular bases de datos complejas sin necesidad de interactuar con código SQL.

Una vez más te recomiendo echarle un vistazo a la serie completa de Ruby desde cero, agradeciendo de antemano todas sus dudas y comentarios en la sección de comentarios.

¡Hasta el próximo capítulo!


Volver a la Portada de Logo Paperblog