#13 Dangers of Model in Session
Dans cet épisode, nous allons vous montrer pourquoi c'est une mauvaise idée de stocker un modèle dans une session.
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
Contrôleur avec des actions qui stockent un modèle en session.
Dans le code précédent, nous avons un contrôleur avec trois actions. La première action, prepare
, cherche le premier User
, le stocke dans la session, puis redirige vers l'action show
. L'action show
prend le premier utilisateur à partir de la session et affiche certaines informations de debug concernant l'utilisateur.
<%= debug(@user) %> <%= link_to 'update', :action => 'update' %>
Le code de la vue show
.
La vue show
a un lien vers l'action update
. L'action update
récupère l'utilisateur courant depuis la session à nouveau, modifie une de ses propriétés puis redirige vers show
. La modificaition effectuée dans update
devrait être temporaire puisqu'elle n'est pas répercutée en base de données. Finallement, update
redirige vers show
donc nous pouvons voir les propriétés de l'utilisateur à nouveau.
Nous allons d'abord à /user/prepare
qui va commencer par mettre l'utilisateur en session puis rediriger vers show
. Dans show
, on peut voir que le nom est correct. Ensuite, nous cliquons sur ‘Update’, la propriété name
est modifiée et nous redirigeons vers show
à nouveau. Le nom est maintenant ‘Foo’, ce qui est différent du nom de l'utilisateur stocké en base de données. Parce que nous avons stocké l'utilisateur en session, n'importe quel changement effectué est persistent au long de la navigation donc chaque fois que l'on récupère l'utilisateur depuis la session, nous verrons l'User
modifié plutôt que celui stocké en base de données. Ce qui peut nous amener à quelques bugs délicats.
NooNoo:ep13 eifion$ script/console Loading development environment (Rails 2.2.2) >> User.first.name => "Eifion" >>
L'User
dans la base de données à toujours le nom ‘Eifion’.
Problèmes avec les validations
Notre modèle User
a une validation qui contrôle que le nom existe (avec validates_presence_of
). Nous allons mettre à jour l'action update
pour initialiser le nom avec une chaine vide, contrôler que l'utilisateur est valide et voir ce qui se passe.
def update @user = session[:user] @user.name = '' @user.valid? redirect_to :action => 'show' end
L'action update
mise à jour.
Maintenant nous avons initialisé le nom avec une chaine vide mais il y a toujours des erreurs. Une fois que nous avons redirigé vers une autre page, les erreurs ne devraient pas persister mais parce que nous avons stocké le modèle en session, elles le font même après redirection. Ceci peut amener à confusion un utilisateur qui pourrait voir des erreurs de validation sur une page et les retrouver quand il reviendra sur la page plus tard.
La bonne façon de faire
Maintenant, nous allons faire ça proprement. Au lieu de stocker le modèle User
en session, nous allons stocker l'id
de l'utilisateur. Le contrôleur mis à jour ressemblera à ça :
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
Contrôleur avec actions qui enregistre un id
en session.
Maintenant, uniquement l'id
est stocké en session et l'utilisateur est récupéré depuis la base de données à chaque requête. Cette fois, le changement fait au nom de l'utilisateur ne persiste pas et reste le même à chaque requête.
Heureusement, cet épisode a montré les dangers du stockage de modèle et autres objets complexes dans une session Rails. L'objet de session devrait seulement être utilisé pour stocker des objets simples comme des tableaux, des chaines ou des entiers.