Django: mixin ou décorateur pour la gestion des accès aux vues
Avec les vues sous forme de fonction en Django, on gérait les permissions à l’aide de décorateurs.
@login_required
def index(self):
return render("index.html")
Depuis l’introduction des vues de type classe, la documentation de django précise que pour avoir le même comportement qu’avec les décorateurs, on peut utiliser des classes mixins (ayant peu d’effets de bord).
class IndexView(LoginRequiredMixin, TemplateView):
template_name = "index.html"
Nous allons voir que le comportement est tout de même différent.
Considérons la vue TextView1
suivante:
class TestView1(LoginRequiredMixin, View):
def dispatch(self, request, *args, **kwargs):
if not request.user.has_perm(app.do_something):
raise PermissionDenied
return super().dispatch(request, *args, **kwargs)
def get(self, request, *args, **kwargs):
return HttpResponse("Contenu view1")
Lors de l’appel à TestView1.as_view()
, la méthode dispatch()
est
appelée. On vérifie que l’utilisateur a bien la permission
app.do_something
, mais rien ne garantit que l’utilisateur est bien
connecté ! Cela se fera seulement après l’appel à super().dispatch()
,
qui va d’abord appeler la méthode dispatch()
de la mixin
LoginRequiredMixin
.
On pourrait donc appeler d’abord la méthode dispatch()
du parent, puis
ensuite vérifier que l’utilisateur a bien les permissions nécessaires.
On considère la vue View2
suivante:
class View2(LoginRequiredMixin, View):
def dispatch(self, request, *args, **kwargs):
response = super().dispatch(request, *args, **kwargs)
if not request.user.has_perm('stage.gerer_stage'):
raise PermissionDenied
return response
def get(self, request, *args, **kwargs):
return HttpResponse("Contenu view2")
Ici il y a deux possibilité:
- Soit on met l’attribut raise_exception à
True
, dans ce cas, lors de l’appel àsuper().dispatch()
, une exception sera renvoyée. - Soit on met l’attribut à
False
, et la réponse renvoyée parsuper().dispatch()
sera une url de redirection vers la page de login. Or, on devrait renvoyer directement vers cette url, mais on vérifie que l’utilisateur a bien la permissionapp.do_something
, même s’il n’est pas connecté!
De plus, en imaginant que l’utilisateur est connecté, l’appel à la
méthode super().dispatch()
va exécuter un tas d’instructions qui, s’il
s’avère que l’utilisateur n’a pas les permissions nécessaires, seront de
toute façon futiles car un raise PermissionDenied
sera de renvoyé.
La dernière méthode, pour se rapprocher des décorateurs utilisés avec
les vues de types fonction (fbv
), on peut utiliser @method_decorator
sur la classe (qui va en réalité s’appliquer sur la méthode
dispatch()
).
@method_decorator(login_required, name='dispatch')
class Test3(View):
def dispatch(self, request, *args, **kwargs):
if not request.user.has_perm('stage.gerer_stage'):
raise PermissionDenied
return super().dispatch(request, *args, **kwargs)
def get(self, request, *args, **kwargs):
return HttpResponse("Contenu view2")
Dans ce cas, la méthode dispatch()
est changée, et son contenu tel
qu’implémenté dans la vue Vue3
ne sera appelé que si l’utilisateur est
connecté.
Un projet avec test est disponible sur framagit/sekun/cbv-mixin-test.