web-dev-qa-db-fra.com

Comment écrire un décorateur personnalisé dans Django?

Le problème -

@is_premium_user
def sample_view:
          .......
          ......

.

49
user677990

Vous n'avez pas besoin d'écrire votre propre décorateur pour cela comme user_passes_test est déjà inclus dans Django.

Et il y a un extrait ( group_required_decorator ) qui étend ce décorateur et qui devrait être assez approprié pour votre cas d'utilisation.

Si vous voulez vraiment écrire votre propre décorateur, il y a beaucoup de bonne documentation sur le net.

Et bien, pour (ré) utiliser le décorateur, placez simplement votre décorateur dans un module sur votre chemin et vous pouvez l'importer depuis n'importe quel autre module.

49
arie

J'ai joué avec les différents liens ci-dessus et je n'ai pas pu les faire fonctionner, puis je suis tombé sur ce lien très simple que j'ai adapté. http://code.activestate.com/recipes/498217-custom-Django-login_required-decorator/

from functools import wraps
from Django.http import HttpResponseRedirect

def authors_only(function):
  @wraps(function)
  def wrap(request, *args, **kwargs):

        profile = request.user.get_profile()
        if profile.usertype == 'Author':
             return function(request, *args, **kwargs)
        else:
            return HttpResponseRedirect('/')

  return wrap

Utilisation de @wraps est meilleur que de remplacer manuellement comme faire wrap.__doc__ = fn.__doc__. Entre autres choses, il garantit que votre fonction wrapper obtient le même nom que la fonction wrappée.

Voir https://docs.python.org/2/library/functools.html

49
PhoebeB

Grâce à arie, la réponse a beaucoup aidé, mais cela ne fonctionne pas pour moi.

Quand j'ai trouvé cet extrait, je l'ai fait fonctionner correctement: http://djangosnippets.org/snippets/983/

Cette solution a fonctionné pour moi:

La fonction d'assistance

Cette fonction a l'avantage d'être réutilisable dans d'autres endroits, en remplacement du user.is_authenticated. Il pourrait par exemple être exposé comme une balise de modèle.

def my_custom_authenticated(user):
    if user:
        if user.is_authenticated():
            return user.groups.filter(name=settings.MY_CUSTOM_GROUP_NAME).exists()
    return False

Le décorateur

Je viens de mettre cela en haut de mon views.py, car il est si court.

def membership_required(fn=None):
    decorator = user_passes_test(my_custom_authenticated)
    if fn:
        return decorator(fn)
    return decorator

Utilisation

@membership_required
def some_view(request):
    ...
5
thnee

Voir des exemples dans Django lui-même:

http://code.djangoproject.com/browser/Django/trunk/Django/contrib/auth/decorators.py

Votre exemple particulier n'est probablement qu'une version de 'user_passes_test' où le test va être l'appartenance au groupe 'premium'.

Pour l'utiliser n'importe où, créez un package python et importez-le à partir de là. Tant qu'il sera sur votre sys.path, il sera trouvé.

2
Spacedman

http://www.makina-corpus.org/blog/permission-required-decorator-Django

j'ai basé le mien sur ce blog.

Collez-le dans un fichier dans le chemin python ou dans une application "util" et importez-le dans les vues:

par exemple

project_dir
|_ app1
|_ app2
|_ utils
   |_ __init__.py
   |_ permreq.py


from util.permreq import permission_required

@permmission_required('someapp.has_some_perm', template='denied.html')
def some_view(request):
    blah blah
1
DTing

Voici une implémentation légèrement différente, qui permet des paramètres supplémentaires afin de spécifier vers quelle page rediriger en cas d'échec de la validation et quel message afficher à l'utilisateur final:

from functools import wraps
from Django.core.urlresolvers import reverse
from Django.http import HttpResponseRedirect
from core.helpers.flash import send_flash_error

def lender_only(redirect_to='plateforme.views.vue_login', error_flash_message=None):
  def inner_render(fn):
    @wraps(fn)  # Ensure the wrapped function keeps the same name as the view
    def wrapped(request, *args, **kwargs):
      if request.context.user.is_authenticated and request.context.user.is_lender:
        return fn(request, *args, **kwargs)
      else:
        if error_flash_message:
          send_flash_error(request, error_flash_message) # Replace by your own implementation

        return HttpResponseRedirect(reverse(redirect_to))
    return wrapped
  return inner_render

# Usage:
@lender_only('vitrine.views.projets', {'message': "Oops, can't go there."})
def render_page_index(request):

Ce guide m'a aidé à passer au travers: https://elfsternberg.com/2009/11/20/python-decorators-with-arguments-with-bonus-Django-goodness/ à côté des réponses précédentes

0
Vadorequest