ActiveRecord::RecordNotFound
Visita este artículo en http://www.estadobeta.com/2007/05/06/activerecordrecordnotfound/
Por Ismael en Patrones de diseño, Ruby & Rails, artículos, tipsAún cuando Ruby on Rails nos facilita enormemente las cosas al proveer una estructura convencional para el desarrollo de aplicaciones web, sigue siendo fácil perderse en los detalles. Por ejemplo, ¿cuál es la mejor forma de mostrar el detalle de un objeto? Sin manejo de errores, la acción de Controlador que busca y muestra un Modelo (una instancia de ActiveRecord) desde la base de datos es trivial:
def show
@product = Product.find params[:id]
@category = @product.category
end
El objeto @product queda disponible para las vistas (templates) HTML o de cualquier formato, en el supuesto de que el parámetro id sea provisto en el request al browser.
Pero qué pasa si esa id no existe en la base de datos? El objeto no será encontrado y nuestra página dará error.
Hay muchas forma de validar la existencia del objeto buscado, pero dado que esta es una acción frecuente en cualquier aplicación, definamos una convención (en el mejor espíritu DRY).
def show
begin
@product = Product.find params[:id]
rescue ActiveRecord::RecordNotFound
flash[:notice] = ‘Ese objeto no existe’
redirect_to :action => ‘index’
else
@category = @product.category
end
end
¿Qué pasó ahí? Cuando ActiveRecord::find no encuentra resultados en la base de dato, emite una excepción de tipo Activerecord::RecordNotFound (que desciende de la clase Exception, de Ruby). Con este conocimiento, y aprovechando el manejo de excepciones particular de Ruby, podemos reencauzar el flujo del programa en caso de que el objeto solicitado no exista en la base de datos.
En Ruby, luego de las cláusulas begin / rescue podemos, opcionalmente, usar else, normalmente usado junto a if. El código despues de else se ejecutará solamente si begin no emitió excepciones.
Al usar rescue sólo para reaccionar ante excepciones de tipo Activerecord::RecordNotFound, permitimos que otro tipo de excepciones surjan hacia capas superiores del programa y sean procesadas por el manejo de excepciones normall de Rails (que generalmente produce una página de error 500).
¿Cómo interceptar esas otras excepciones para generar páginas de error más amigables? Tema para otro artículo.
- Servicios:
- Comentarios RSS
- Menear!
- Del.icio.us

5/7/2007 at 10:47 am
otra opción es usar exists? (hace lo mismo, pero me parece más limpio y legible).
if Product.exists?(params[:id])
*busco los datos*
else
*aviso que no existe*
end
Saludos!
5/7/2007 at 2:22 pm
Cierto. El problema que veo con eso es que te obliga a hacer 2 queries a la DB.
if Product.exists?(params[:id]) #1er query
@product = Product.find(params[:id]) #2do query
end
En realidad, la técnica que describo es más útil cuando la usas en conjunto con rescue_action_in_public, de ActionController, para generar páginas 404 si el modelo no existe:
def rescue_action_in_public( excepcion )
case excepcion
when ActiveRecord::RecordNotFound
render :file => RAILS_ROOT + '/public/404.html',
:status=> '404 Not Found'
else
super( exception )
end
end
5/9/2007 at 12:36 am
otra cosa que podrías hacer es un ‘hack’ a AR.
class ActiveRecord::Base
def find_safe(*args)
begin
find(args)
rescue ActiveRecord::RecordNotFound
nil
end
end
end
Si no encuentra el ‘record’ con ese ID, retorna nil. Lo podrias chequear con:
if @product
#lala
else
#lolo
end
aunque voy mas por usar el rescue_action_in_public si de algo mas serio se trata.
Saludos!, Héctor.
5/11/2007 at 9:19 pm
Buena solución H!
5/24/2007 at 9:26 pm
Me parece que Rails cuando esta en enviroment “production” oculta los mensajes de error y coloca una página que dice “Aplication Error”, es decir los usuarios finales no verán los errores feos por lo qu eno es necesario hacer eso.
5/25/2007 at 12:10 pm
SI, pero eso por defecto envía un error 500 al browser. Si el usuario no encuentra lo que estás buscando, probablemente quieres envíar un error 404 (”página no encontrada”) y tener más control sobre el diseño de la página de error. por ejemplo: mostrar información relacionada, un buscador, el mapa del sitio, etc.
5/30/2007 at 1:55 am
Definitivamente el try catch de Ismael es la mejor solucion.
6/1/2007 at 6:09 pm
Beto: por qué? Beto ha saber?
6/1/2007 at 7:21 pm
6/2/2007 at 3:21 am
Bueno porque considero que es la solución más limpia y eficiente para resolver el problema. Además que extender la clase ActiveRecord para solucionar este problema (pequeño por lo demas) como que no me gusta de onda.. xD.
7/22/2007 at 2:08 am
Lo más limpio es no hacer nada en el controlador:
def show
@product = Product.find( params[:id] )
end
En tu application controller usas
rescue_action_in_publicpara atrapar las excepciones de tipo RecordNotFound y presentar la página de error correspondiente.8/17/2007 at 5:09 pm
Lo anterior sólo funciona si usas
ActiveRecord:.Base#find, pasándole una ID como único argumento. Si quieres usar otro campo como (como el nombre o “permalink”) y mantener este patrón de diseño, puedes usar mi plugin sluggable_finder10/2/2007 at 9:06 am
[…] con excepciones como ActiveRecord::RecordNotFound, desde interceptarlas en cada acción hasta capturarlas en un sólo punto con rescue_action_in_public. Ahora, ActionController provee una macro para definir métodos […]
3/19/2008 at 6:59 am
[…] Las ID’s en una base de datos debieran ser únicas; Rails aprovecha esto y por convención emite una excepción ActiveRecord::RecordNotFound si el objeto con esa ID no existe. Esto es útil porque podemos usar esa particularidad del método find para normalizar nuestro manejo de errores. […]