50 votes

Authentification et Ajax dans Django - URLs nécessitant une connexion

Je veux ajouter quelques Ajax -naturelle à mon site web codé en Django.

Dans mon code Django, j'utilise le @login_required du décorateur de django.contrib.auth.decorators pour indiquer quelle vue nécessite une authentification. Le comportement par défaut lorsqu'un utilisateur non authentifié clique dessus est de le rediriger vers la page de connexion, puis de lui transmettre la page cible.

Ce que j'ai vu sur certains sites, et que j'ai vraiment apprécié, c'est que lorsque l'utilisateur clique sur un lien menant à un endroit réservé aux utilisateurs connectés, au lieu d'être redirigé vers une page de connexion, il obtient une fenêtre popup (via JavaScript) lui demandant de se connecter ou de s'enregistrer. Il n'y a pas de redirection, et l'utilisateur n'a pas besoin d'utiliser la touche "retour" s'il décide qu'il n'aime pas suffisamment le site pour perdre son temps à s'enregistrer.

La question qui se pose est donc la suivante : comment gérer la tâche consistant à marquer automatiquement certains liens comme étant "restreints" afin que JavaScript puisse gérer leur contenu. onclick et afficher une fenêtre contextuelle "Veuillez vous connecter" ?

57voto

Eric Walker Points 2494

Je suis confronté au même problème et, comme vous, j'aimerais disposer d'un décorateur simple pour envelopper une vue Django ajax afin de gérer l'authentification de la même manière que pour les autres vues. Une approche qui me semble prometteuse consiste à utiliser un tel décorateur en conjonction avec du JavaScript qui recherche une certaine valeur dans la réponse.

Voici premièrement projet révisé du décorateur :

from functools import wraps

def ajax_login_required(view_func):
    @wraps(view_func)
    def wrapper(request, *args, **kwargs):
        if request.user.is_authenticated():
            return view_func(request, *args, **kwargs)
        json = simplejson.dumps({ 'not_authenticated': True })
        return HttpResponse(json, mimetype='application/json')
    return wrapper

Voici la vue :

@ajax_login_required
def ajax_update_module(request, module_slug, action):
    # Etc ...
    return HttpResponse(json, mimetype='application/json')

Et voici le JavaScript (jQuery) :

$.post('/restricted-url/', data, function(json) {
    if (json.not_authenticated) {
        alert('Not authorized.');  // Or something in a message DIV
        return;
    }
    // Etc ...
});

EDIT : J'ai essayé d'utiliser functools.wraps comme suggéré. Je n'ai pas encore utilisé ce décorateur dans du code fonctionnel, donc attention aux bugs possibles.

6 votes

Il est peut-être utile de préciser que vous pouvez utiliser la fonction functools.wraps décorateur autour de def wrap pour éviter les affectations à wrap.__doc__ , wrap.__dict__ y wrap.__name__ (ce qui n'est pas mentionné dans cette réponse mais devrait être fait). Voir docs.python.org/library/functools.html#functools.wraps

0 votes

@Anders : s'il vous plaît, allez-y et modifiez la réponse pour incorporer le changement. Je n'ai pas travaillé en Python depuis un moment, et je ne veux pas tout gâcher.

2 votes

La vue ne devrait-elle pas plutôt envoyer un état HTTP 401 à la place ?

5voto

S.Lott Points 207588

On dirait une possibilité de modèle de page.

  1. Vous pourriez passer un LINK_VIA (ou autre) que vous fournissez comme onClick="return popup(this, 'arg')" o None . Chaque lien serait <A HREF="link" {{LINK_VIA}}>some text</a> .

    • Pour les sessions anonymes, LINK_VIA a une valeur.
    • Pour les sessions connectées, LINK_VIA est Aucun
  2. Vous pourriez utiliser un {% if %} autour de votre <A HREF=...> tags. Cela semble verbeux.

  3. Vous pouvez écrire votre propre balise personnalisée avec pour {% link_via %} . Je ne suis pas assez familier avec ce système, mais vous pouvez fournir le lien et le texte sous forme de chaînes de caractères et votre balise peut générer l'un des deux types de liens.

0 votes

Ce que je me demande, c'est comment déterminer, du point de vue du modèle, si la vue vers laquelle le lien mène est décorée avec @login_required ou non...

0 votes

Le modèle n'a pas de liens aléatoires. Il comporte des liens spécifiques que vous avez spécifiquement conçus et codés dans le modèle. Je vous suggère de modifier chaque lien que vous avez spécifiquement mis dans le modèle.

5voto

hasenj Points 36139

Je suis d'accord avec S.Lott

Faites une vérification dans le modèle, si l'utilisateur est connecté, mettez simplement le lien comme d'habitude, sinon, mettez quelque chose comme

<a href="{{link}}" onclick="return login_popup()"> 

où login_popup renverrait false si l'utilisateur dit cancel.

Cela pourrait probablement être fait beaucoup plus facilement en Jinja2 à travers son macros .

Si le modèle ne sait pas quelles urls requièrent une connexion de l'utilisateur, vous devez probablement revoir votre conception.

Si vous devez le faire, je suppose que vous pouvez faire la même chose que le distributeur d'url de Django pour découvrir la fonction de vue.
voir : django.core.urlresolvers

Une fois que vous avez saisi la fonction de vue, vous pouvez vérifier si elle est décorée avec @login_required.

Cela se ferait probablement dans une balise personnalisée.
Si vous utilisez Jinja2, vous n'aurez pas besoin de la balise, il suffit d'implémenter la fonction et de l'exposer à l'environnement, c'est simple mais vous devrez lire un peu l'API de Jinja2).

0 votes

Je suis sauvagement en retard à la fête sur ce coup-là, mais peu importe : Ce n'est pas idéal uniquement parce que l'utilisateur aurait pu suivre un lien direct vers la page en question, auquel cas la méthodologie de la popup de connexion requise ici se brisera.

2voto

Warren Spencer Points 136

Construit à partir de Eric Walker mais pour Django 2.0

# Standard Imports
import functools
import django.http

def ajax_login_required(view_func):
    @functools.wraps(view_func)
    def wrapper(request, *args, **kwargs):
        if request.user.is_authenticated:
            return view_func(request, *args, **kwargs)

        return django.http.JsonResponse('Unauthorized', status=401, safe=False)

    return wrapper

1voto

Voici la version proposée du décorateur avec wrap.__doc__ , wrap.__name__ .

from functools import wraps

def ajax_login_required(function):
    def wrap(request, *args, **kwargs):
        if request.user.is_authenticated():
            return function(request, *args, **kwargs)
        json = simplejson.dumps({ 'not_authenticated': True })
        return HttpResponse(json, mimetype='application/json')  
    wrap.__doc__ = function.__doc__
    wrap.__name__ = function.__name__
    return wrap

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X