lunes, 17 de diciembre de 2007

rescue Exceptions en Rails


Acá en este caso trato de explicar algo que puede pasarles cuando tienen tablas relacionadas donde campos de unas tabla(id por lo general) son claves foráneas de otras y el scaffold no genera métodos que prevenga estos problemas de tener restringidos el borrado de ciertos registros de unas tablas por depender de estos la integridad del registro de otra/s tablas. El problema generalmente lo ves cuando la aplicación explota o te lo hace saber un test de tu aplicación(que debería ser lo correcto). En el caso de una agenda donde tenemos contactos que tienen un nombre+apellido, una dirección postal, una dirección de mail, un teléfono y una ciudad.. lo conveniente según mi punto de vista es tener por un lado una tabla de "contactos" y otra tabla de "ciudades". La tabla con los contactos necesita tener una foreign key para traer consigo el código de la ciudad a la que pertenece el contacto, entonces el caso resulta como sigue.. El caso práctico es que tengo dos tablas donde en una estan los contactos y en otra las ciudades a las que pertenecen.


Tabla «public.contacts»

Columna | Tipo | Modificadores
---------+------------------------+------------------------------------------------------
id | integer | not null default nextval('contacts_id_seq'::regclass)
name | character varying(255) | not null
address | character varying(255) | not null
email | character varying(255) | not null
phone | character varying(255) | not null
city_id | integer | not nullÍndices:

«contacts_pkey» PRIMARY KEY, btree (id)

Restricciones de llave foránea:

«city_id_fkey» FOREIGN KEY (city_id) REFERENCES cities(id)

y otra tabla donde estan las ciudades

Tabla «public.cities»

Columna | Tipo | Modificadores
---------+------------------------+-----------------------------------------------------
id | integer | not null default nextval('cities_id_seq'::regclass)
name | character varying(255) | not null
cp | integer | not null default 0

Índices:

«cities_pkey» PRIMARY KEY, btree (id)

Cómo ven tenemos restricciones de una llave foránea 'city_id' en la tabla "contacts" que hace referencia al campo 'id' de la tabla "cities". Si tenemos estos datos en las tablas :

# SELECT * from cities;

id | name | cp
----+---------+------
1 | La Paz | 3900
7 | Rosario | 3001

(2 filas)


# SELECT * from contacts;

id | name | address |
email | phone | city_id
----+--------------+------------------+---------------------------------+--------------+---------
2 | cesar diaz | Saenz Penia 1129 | cdiaz@gmail.com | 03437 438837 | 1
3 | pepe argento | san martin 2323 | pargento@argentino.com.ar | 48937438943 | 7

(2 filas)

Yo tengo la posibilidad de borrar un registro de tabla "cities" .....






en el ejemplo el registro de la ciudad "Rosario" tiene el id = 7
y se hace referencian a este registro desde la tabla "contacts" para el "id = 3"
el método "destroy" dentro del cities_controller.rb para borrar un registro esta
escrito como sigue.....



me da un lindo mensaje de error como el siguiente...






ActiveRecord::StatementInvalid in CitiesController#destroyPGError: ERROR: update o delete en «cities» viola la llave foránea «city_id_fkey» en «contacts»

DETAIL: La llave (id)=(7) todavía es referida desde la tabla «contacts».
: DELETE FROM cities
WHERE "id" = 7

Para evitar esto refactorizo el metodo y trato el error utilizando begin , rescue .. end







Bueno.. como ven en este código.. trata de realizar el borrado del registro en la tabla 'cities' buscando para el objeto "City" el registro que tiene el "params[:id]" y le aplica el método "destroy" ... pero en caso de que esto no sea factible por violación de clave foránea en este caso la linea "rescue ActiveRecord::StatementInvalid => error" detecta el error y envió un mensaje al usuario con un mensaje adecuado "flash[:error] = 'Un registro depende de este.'" y luego vuelve a listar las ciudades "redirect_to :action => 'list'"... A esto seguro que tendría que agregar la captura del error exacto.. pero es una solución mejor que hacer explotar la aplicación como lo hacia antes.... Quizas hay mejores formas que hacer todo este quilombo para solucionar este problema, pero bueno.. a medida que voy aprendiendo mas de Rails me parece bueno transmitirlo y si estoy equivocado o las cosas se pueden hacer mejor me gustaria aprender de los demas.. :-) ... Espero que les ayude o les de idea de como resolver mejor algun problema... cualquier mejora sera bienvenida .... nos vemos... saludos

No hay comentarios: