Active Record
Visita este artículo en http://www.estadobeta.com/2006/05/02/active-record/
Por Ismael en Patrones de diseño, Ruby & Rails, artículos, educaciónDescripción de Active Record y ejemplos de implementación y uso.
De todos los Patrones de Diseño, Active Record es probablemente el que recibe más atención recientemente.
Domain Model
Active Record es una extensión del patrón Domain Model (”Modelo de Dominio”), que se entiende como una clase o un grupo de clases que representan a “objetos” o responsabilidades particulares en la aplicación. Para una aplicación que despliega un catálogo de productos, por ejemplo, cada producto podría ser una instancia de la clase “Producto” (el catálogo mismo podría ser, a su vez, un objeto que contiene muchos objetos “Producto”).
Frecuentemente, una implementación de Domain Model representa los datos contenidos en una o más tablas de una base de datos. Por ejemplo, para una tabla que contiene productos en un catálogo (representada aquí gráficamente):
| id | precio | nombre | descripción |
|---|---|---|---|
| 1 | 180.000 | iPod Nano | reproductor portátil de sonido |
| 2 | 40.000 | Adidas Chile 62 | zapatillas deportivas |
| 3 | 450.000 | FujiPix F601Z | cámara fotográfica digital |
podemos escribir una clase que represente a cada producto y sus atributos*1 (Domain Model):
class Producto {
attributo id
attributo precio
attributo nombre
attributo descripción
}
Esta sencilla clase nos permitiría disponer de los productos en nuestra aplicación y acceder a sus atibutos:
ipod = new Producto //la creación de estos objetos es comúnmente manejada por otros patrones como Factory.
precio = ipod.precio
nombre = ipod.nombre
...
Active Record: Domain Model con vitaminas
Pues bien, Active Record abstrae aún más la base de datos al agregar a Domain Model la lógica necesaria para manipular esos datos. En el ejemplo:
ipod = Producto.find(1)
ipod.precio = 300.000
ipod.save()
vemos que ahora las instancias de Producto incluyen el método find() (que permite encontrar un producto por su ID en la base de datos) y el método save(), que actualiza los datos correspondientes a ese producto en la base de datos. Estos métodos comúnmente generan el SQL necesario e incluyen las utilidades para conectarse e interactuar con la base de datos. Así mismo, una implementación de Active Record puede tener utilidades que nos faciliten el trabajo en aplicaciones concretas. Imaginemos, por ejemplo, que de cada producto necesitamos obtener el precio + IVA (impuesto de 19% aquí en Chile). Para esto podemos agregar el método adecuado a nuestra clase Producto.
class Producto hereda de activeRecord {
attributo id
attributo precio
attributo nombre
attributo descripción
metodo getPrecioConIva(){
return (this.precio * 1.19)
}
}
y creamos un objeto de clase Producto del que podemos obtener el valor total cómodamente:
ipod = Producto.find(34)
precio_con_iva = ipod.getPrecioConIva()
getPrecioConIva() se encarga de calcular el valor internamente, dejando el código cliente mínimo y claro. De esta forma se pueden encapsular operaciones mucho más sofisticadas sobre los datos tras una interfaz sencilla y fácil de usar.
ORM
Pero ¿ qué pasa con las tablas relacionadas? Normalmente un Modelo de Dominio estará compuesto por datos alojados en varias tablas de una base de datos relacional. Imaginemos que nuestros Productos pertenecen a una o más Categorías. Veamos la tabla “categorias” en nuestra base de datos.
| id | nombre | descripción |
|---|---|---|
| 1 | linea blanca | artículos de línea blanca |
| 2 | audio | artículos de audio, música |
| 3 | ropa | artículos de vestir |
Un Producto puede pertenecer a una o más Categorías. Así mismo, cada categoría puede tener uno o más productos. Este tipo de relaciones se suelen articular por medio de una tabla intermedia:
Tabla categorias_productos
| categoria_id | producto_id |
|---|---|
| 2 | 1 |
| 3 | 2 |
Ok. Las cosas se complican. Necesitamos 3 tablas en la base de datos para expresar la relación entre Productos y Categorías. Asumiendo que hemos creado una clase categoria similar a Producto para encapsular los registros de la tabla categorias, surge el problema de describir en nuestras clases la interdependencia entre productos y categorías.
Aquí es donde comienza a relucir el patrón Active Record. Una de las virtudes del patrón es representar de forma Orientada a Objetos los datos de una Base de Datos Relacional - modelo conocido también como ORM o “Object-Relational Mapping” - , definiendo interfaces sencillas para acceder y manipular esos datos.
Instancia de Producto con atributo categorias (una colección de objetos Categoria):
ipod = Producto.find(1)
categorias = ipod.categorias
En este otro ejemplo, buscamos todos los Productos pertenecientes a la categoría “audio” (id 2).
categoria = Categoria.find(2)
productos = categoria.productos
En la mayor parte de las implementaciones, Active Record se encarga internamente de recoger los objetos relacionados del modelo de datos, definiendo las colecciones de objetos dependientes automáticamente. La implementación del framework Ruby on Rails es especialmente interesante. En ella, el nombre de la clase se “mapea” directamente al nombre de la tabla en la base de datos*2, y en la definición de la clase se declaran los objetos dependientes, que a su vez encuentran sus tablas correspondientes de forma transparente:
# definición de la clase
class Producto < ActiveRecord::Base
has_and_belongs_to_many :categorias
end
#código cliente de ejemplo
ipod = Producto.find_by_nombre("ipod")
#Creamos una nueva categoría y la agregamos al producto Ipod
nueva_categoria = Categoria.create("computación","Artículos informáticos")
ipod.categorias.add(nueva_categoria)
#actualizamos el producto en la base de datos.
#Esto creará la relación correpondiente en la tabla categorias_productos.
ipod.save()
La línea has_and_belongs_to_many :categorias declara el tipo de relación entre Productos y Categorias. Al momento de instanciar un objeto, Active Record asumirá que existe una tabla intermedia categorias_productos (en orden alfabético) y la usará para encontrar los registros dependientes en la tabla categorias, instanciando un objeto de clase Categoria para cada uno y agregándolos a la coleción Producto.categorias. La implementación de Active Record de Ruby on Rails también incluye declaraciones como has_many, belongs_to y has_one que expresan los tipode de relaciones entre tablas más comunes en el diseño de aplicaciones.
Observaciones
Active Record enfenta el problema de los
Links
- Active Record en Wikipedia
- Active Record en Ruby on Rails
- Catálogo de Patrones de Diseño de Martin Fowler
- Ruby on Rails
*2 Ruby on Rails se basa por completo en la premisa de “Convención por sobre Configuración”, usando convenciones de nombres y Reflexión de clases para automatizar el desarrollo de aplicaciones lo más posible.
- Servicios:
- Comentarios RSS
- Menear!
- Del.icio.us

8/7/2006 at 4:23 pm
[…] Ingenieros del mundo: denle una oportunidad a Rails (o por lo menos a las ideas que lo sustentan). De todas maneras nada puede ser peor que lidiar con ASP cada dia. […]
6/22/2007 at 10:20 pm
No me podia quedar callado y lo tengo que decir… buen artículo. Por cierto para todos aquellos que quieren probar algo similar a Ruby on Rails pero para el querido y muchas veces menospreciado PHP les recomiendo ampliamente CakePHP
6/25/2007 at 12:29 am
Interesante artículo, pero tengo una duda en este pedazo de código:
categoria = Categoria.find(2)
productos = categoria.productos
Como hago para obtener la lista productos ordenada por algún atributo??
Gracias.
6/25/2007 at 1:23 am
gameX:
Puedes ordenar la colección de resultados con Ruby
pero también puedes agregar SQL al “finder”
En realidad, lo mejor que puedes hacer es definir el orden en la asociación misma:
Hay mucho más en la documentación de ActiveRecord.
6/25/2007 at 1:46 am
Muchas gracias Ismael.