#13 Dangers of Model in Session
En este episodio mostraremos porque es una mala idea almacenar un modelo en la sesión.
class UserController < ApplicationController def prepare session[:user] = User.find(:first) redirect_to :action => :'show' end def show @user = session[:user] end def update @user = session[:user] @user.name = 'Foo' redirect_to :action => 'show' end end
El controlador con acciones que almacenan un modelo en la sesión.
En el código de arriba tenemos un controlador con tres acciones. La primera acción, prepare
(prepara), busca el primer usuario, lo almacena en la sesión y redirecciona a la acción show
. La acción show
trae al usuario actual de la sesión y devuelve información de debug sobre ese usuario.
<%= debug(@user) %> <%= link_to 'update', :action => 'update' %>
El código de la vista show.
La vista show
tiene un link a la acción update
. Nuevamente, la acción update
obtiene el usuario actual de la sesión, cambia una de sus propiedades y redirecciona a la acción show
. El cambio hecho en la acción update debería ser temporal debido a que no es persistido a la bases de datos. Finalmente, la acción update
redirecciona a la acción show
para que ,nuevamente, podamos ver las propiedades del usuario.
Primero, vamos a ir a /user/prepare
(primera ventana de la imagen superior) que coloca al primer usuario en la sesión y redirecciona a la acción show
(segunda ventana de la imagen superior). En la acción show
podemos ver que el nombre es correcto. Entonces clickeamos 'Update', la propiedad nombre (name) del usuario es cambiada y somos redireccionados a la acción show
(tercera ventana de la imagen superior). Ahora, el nombre es 'Foo', que es diferente al nombre de usuario que existe en la base de datos. Como tenemos al usuario almacenado en la sesión, cualquier cambios que le hagamos son persistidos a través de los requests. Por lo que cada vez que obtengamos al usuario de la sesión, estaremos viendo al usuario modificado, en vez del usuario almacenado en la base de datos. Esto puede llevarnos a que tengamos algunos errores (bugs) a futuro.
NooNoo:ep13 eifion$ script/console Loading development environment (Rails 2.2.2) >> User.first.name => "Eifion" >>
El usuario en la base de datos aun se llama 'Eifion'.
Problemas con validaciones
Nuestro modelo User
tiene una validación que verifica que el nombre exista (con validates_presence_of
). Vamos a actualizar la acción update
para que modifique el nombre a una cadena vacía, verifique que el usuario sea válido y veamos que pasa.
def update @user = session[:user] @user.name = '' @user.valid? redirect_to :action => 'show' end
La acción update actualizada.
Ahora, hemos cambiado el nombre por una cadena vacía, pero aún hay errores. Una vez que nos redirija a otra página, los errores no debieran ser persistidos pero, como estamos almacenando el modelo en la sesión, los errores están siendo persistidos, incluso después de ser redireccionado. Esto puede causar confusión a un usuario que podría ver errores de validación en una página y aún encontrarlos allí al regresar más tarde a la página.
La correcta forma de hacerlo
Ahora, lo haremos correctamente. En vez de almacenar el modelo User en la sesión, vamos a almacenar el id del usuario. El código del controlador actualizado se va a ver así:
class UserController < ApplicationController def prepare session[:user_id] = User.find(:first).id redirect_to :action => :'show' end def show @user = User.find(session[:user_id]) end def update @user = User.find(session[:user_id]) @user.name = 'Foo' redirect_to :action => 'show' end end
El controlador con las acciones que almacenan una id en la sesión.
Ahora, solo el id
es almacenado en la sesión y el usuario es buscado de la base de datos en cada request (pedido). Esta vez, el cambio realizado al nombre de usuario no es persistido y permanece igual en todos los requests.
Espero que este capítulo haya mostrado los peligros de almacenar modelos y otros objetos complejos en una sesión Rails. El objeto sesión debería solamente ser utilizado para almacenar objetos simples tales como un arreglo, cadenas o enteros.