¿Cómo crear un decorador personalizado en Django?

5 minutos de lectura

avatar de usuario de user677990
usuario677990

Estoy tratando de crear un decorador personalizado en Django pero no pude encontrar ninguna forma de hacerlo.

# "views.py"

@custom_decorator 
def my_view(request):
    # .......

Entonces, ¿cómo puedo crearlo en Django? y ¿dónde debería ponerlo para poder usarlo en cualquier parte de mi proyecto Django?

Avatar de usuario de PhoebeB
PhoebeB

Jugué con los diversos enlaces anteriores y no pude hacerlos funcionar y luego encontré este realmente simple que 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("https://stackoverflow.com/")

  return wrap

Usando @wraps es mejor que anular manualmente como hacer wrap.__doc__ = fn.__doc__. Entre otras cosas, garantiza que su función contenedora obtenga el mismo nombre que la función envuelta.

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

  • Esta debería ser la respuesta aceptada, ¡pulgares arriba! Traté de aprovechar el decorador user_passes_test pero me perdí, esto salvó el día.

    – Radtek

    16 de septiembre de 2014 a las 0:59

  • @radtek tengo wrap() takes at least 1 argument (0 given). ¿Alguna pista para solucionarlo?

    – Ardiano

    16 de agosto de 2015 a las 16:14

  • Tendría que ver su código, pero lo más probable es que no haya pasado la solicitud a la función que está decorando.

    – Radtek

    18 de agosto de 2015 a las 17:53

  • la @wraps El método es solo para documentos, por lo que podría omitirlo, ¿verdad?

    – Sven

    3 de mayo de 2020 a las 14:07

avatar de usuario de arie
ario

No tienes que escribir tu propio decorador para esto como user_passes_test ya está incluido en Django.

Y hay un fragmento (group_required_decorator) que amplía este decorador y que debería ser bastante apropiado para su caso de uso.

Si realmente quieres escribir tu propio decorador, entonces hay un montón de buena documentacion en la red.

Y bueno, para (re)utilizar el decorador simplemente coloque su decorador en un módulo en su ruta y puede importarlo desde cualquier otro módulo.

  • def souk_required(): “””Requiere que el usuario sea miembro de al menos uno de los grupos pasados.””” def has_souk(u): if u.is_authenticated(): if bool(SoukUsers.objects.get(person = u) ): return True return False(u) return user_passes_test(has_souk)

    – usuario677990

    29 de marzo de 2011 a las 8:18

  • da este error – souk_required no toma argumentos (1 dado)

    – usuario677990

    29 de marzo de 2011 a las 8:19

  • Hm .. Accidentalmente voté a favor de su comentario 😉 Bueno, eliminó el argumento esperado de la definición de su función y, por lo tanto, recibió el error dado. Entonces, ¿qué hay de crear un grupo de “usuarios premium” y agregar sus usuarios a ese grupo? Luego puede usar el fragmento tal como está y simplemente pasar el nombre de su grupo.

    – arie

    29 de marzo de 2011 a las 8:31

  • No es tu culpa que nos pase a los mejores; ]…. de todos modos muchas gracias hombre… Te debo mucho tiempo.

    – usuario677990

    29 de marzo de 2011 a las 8:42

Gracias a arie, la respuesta ayudó mucho, pero no me funciona.

Cuando encontré este fragmento, conseguí que funcionara correctamente: http://djangosnippets.org/snippets/983/

Esta solución funcionó para mí:

La función auxiliar

Esta función tiene la ventaja de ser reutilizable en otros lugares, como reemplazo directo de user.is_authenticated. Podría, por ejemplo, exponerse como una etiqueta de plantilla.

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

el decorador

Acabo de poner esto en la parte superior de mi views.pyya que es muy corto.

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

usándolo

@membership_required
def some_view(request):
    ...

Ver ejemplos en Django mismo:

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

Su ejemplo particular es probablemente solo una versión de ‘user_passes_test’ donde la prueba será membresía del grupo ‘premium’.

Para usar en cualquier lugar, cree un paquete de python e impórtelo desde allí. Siempre que esté en su sys.path, se encontrará.

Aquí hay una implementación ligeramente diferente, que permite parámetros adicionales para especificar a qué página redirigir cuando falla la validación y qué mensaje mostrar al usuario 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):

Esta guía me ayudó a superarlo: https://elfsternberg.com/2009/11/20/python-decorators-with-arguments-with-bonus-django-goodness/ junto con las respuestas anteriores

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

basé el mío en esa publicación de blog.

Pegue eso en un archivo en la ruta de Python o en una aplicación “util” e impórtelo a las vistas:

p.ej

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

from django.contrib.auth.decorators import user_passes_test
from django.core.exceptions import PermissionDenied

def perm_group_required(group, login_url="https://stackoverflow.com/", raise_exception=False):
    def check_group(user):
        if user.groups.filter(name=group).exists():
            return True
        # In case the 403 handler should be called raise the exception
        if raise_exception:
            raise PermissionDenied
        return False
    return user_passes_test(check_group, login_url=login_url)


@perm_group_required('add_customer_group')
#group name="add_customer_group"
def employee_add_customer(request):
    ##logic
    ...

  • Que es #group name="add_customer_group"? Cualquier enlace sobre cómo hacer un decorador con un objeto que tiene un BooleanField?

    – Usuario anónimo

    6 ago a las 4:36


  • name=”add_customer_group” es un nombre de grupo de permisos y ese nombre de grupo de permisos se asigna a un usuario autenticado; si algún usuario autenticado tiene este permiso, puede acceder a la función de vista

    – Prashan Basantia

    6 ago a las 6:16


¿Ha sido útil esta solución?