Cómo Usar el Método Includes() de ActiveRecord en Rails

Algo que tiende a frustrarme de algunas guías o documentación oficial es que solo dan el uso más básico a lo que explican. Pues, bueno, uno como desarrollador debe jugar con lo que hay para descubrir más opciones pero a veces no vendría mal un poco de ayuda en temas más complejos.

Mi insatisfacción estaba dada por la sección de las guías oficiales de Rails donde explican cómo evitar la consulta de tipo N+1. Este tipo de llamado a la base de datos ocurre como cuando por ejemplo queremos mostrar los Y valores que están relacionados con X tabla. Más preciso, a continuación.

El Súper Blog XYZ

desarrollo en ruby on rails

Supongamos que tenemos un blog(el típico ejemplo de siempre) donde hay una tabla(o modelo en ActiveRecord) Artículo y otra Comentario. Donde:

  • Artículo tiene muchos Comentarios
  • Comentario pertenece a un Artículo

Si quisiéramos, en una vista, mostrar los comentarios de un artículo en particular, lo que un desarrollador podría hacer es:

Habiendo definido en el controlador ArticlesController la variable @article = Article.find(1).

<% @article.comments.each do |comment| %>

<%= comment.content %>

<% end %>

Lo anterior dispara el caso de la consulta N+1. Ya que hará una consulta para obtener el artículo y luego hará consultas adicionales por cada comentario que este tenga.

Entra includes()

Aquí es donde todo se pone interesante. En las guías, en la documentación oficial se dice que para resolverlo se debe escribir algo como:

Article.includes(:comments)

para evitar el caso de la consulta N+1. Sin embargo, siempre me abstuve de hacerlo porque ese código de arriba lo que haría sería traer todos los artículos con todos sus comentarios. Es ahí donde digo que las guías se quedaban cortas.

Si intentaba algo como:

article1= Article.find(1).includes(:comments)

Obtenía un error de ActiveRecord. Más precisamente NoMethodError. ¿Qué quiere decir? Y para mi entender, que includes() es un método de clase y no de instancia.

Y entonces, ¿cómo se usa el includes()?

Gracias a un compañero de trabajo aprendí cómo usarlo debidamente para obtener los registros relacionados de un objeto en particular y no de toda una colección.

@article = Article.includes(:comments).find(1)

De esa forma, se están tomando todos los artículos y todos sus comentarios (eager load) pero además, a la colección resultado* le hago una consulta find para encontrar el objecto que quiero.

Ahora, cuando quiera escribir este código:

<% @article.comments.each do |comment| %>

<%= comment.content %>

<% end %>

Ya no habrá consulta extra por cada comentario en el artículo.

  • Toda consulta hecha con métodos de ActiveRecord, devuelve un objecto ActiveRecord:: al cual se le pueden encadenar más consultas.

¿Qué es eager loading?

Autor: cesc1989

Ingeniero de Sistemas que le gusta escribir y compartir sobre recursos que considera útiles, además que le gusta leer manga y ver anime.

Deja un comentario

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