2009-12-05 16 views
5

que tienen este decorador, que se utiliza para decorar una vista de Django cuando yo no quiero que el fin de ser ejecutado si el argumento share es True (manejado por el middleware)añadiendo un argumento para un decorador

class no_share(object): 
    def __init__(self, view): 
     self.view = view 

    def __call__(self, request, *args, **kwargs): 
     """Don't let them in if it's shared""" 

     if kwargs.get('shared', True): 
      from django.http import Http404 
      raise Http404('not availiable for sharing') 

     return self.view(request, *args, **kwargs) 

la actualidad funciona así:

@no_share 
def prefs(request, [...]) 

Pero estoy queriendo ampliar la funcionalidad un poco, por lo que va a trabajar de esta manera:

@no_share('prefs') 
def prefs(request, [...]) 

Mi pregunta es ¿cómo puedo modificar esta clase de decorador para que acepte argumentos adicionales?

Respuesta

7

Espero this artículo de Bruce Eckel ayuda.

UPD: Según el artículo de su código se verá así:

class no_share(object): 
    def __init__(self, arg1): 
     self.arg1 = arg1 

    def __call__(self, f): 
     """Don't let them in if it's shared""" 

     # Do something with the argument passed to the decorator. 
     print 'Decorator arguments:', self.arg1 

     def wrapped_f(request, *args, **kwargs): 
      if kwargs.get('shared', True): 
       from django.http import Http404 
       raise Http404('not availiable for sharing') 
      f(request, *args, **kwargs)    
     return wrapped_f 

a usarse según se desee:

@no_share('prefs') 
def prefs(request, [...]) 
1
class no_share(object): 
    def __init__(self, foo, view): 
     self.foo = foo 
     self.view = view 
+0

debería haber mencionado, he intentado ya, pero creo que Django funciona de forma diferente o algo porque eso no funcionó .Intenté poner 'print 'blah' 'en el init, pero todo lo que obtuve fue un montón de' blah's 'cuando inicié el servidor de desarrollo. No más 'bla' después de eso ... – priestc

+1

Eso es lo esperado, __init__ solo se llama una vez, según la definición de cada función de vista que decore con esto. Se llama a la llamada __ cada vez que su servidor ejecuta la función de vista para responder a una solicitud. Esta respuesta va en la dirección correcta, consulte mi respuesta para obtener una respuesta más completa específica para su caso. – taleinat

0

Puesto que parece que está recibiendo este mal en alguna parte , aquí hay un ejemplo más completo que puede ayudarte a ver lo que estás haciendo mal. Usar esto como un drop-in debería funcionar.

class no_share(object): 
    def __init__(self, view, attr_name): 
     self.view = view 
     self.attr_name = attr_name 

    def __call__(self, request, *args, **kwargs): 
     """Don't let them in if it's shared""" 

     if kwargs.get(self.attr_name, True): 
      from django.http import Http404 
      raise Http404('not availiable for sharing') 

     return self.view(request, *args, **kwargs) 
4

El Bruce Eckel article que Li0liQ mencionado debería ser útil para calcular esto. Los decoradores con y sin argumentos se comportan de forma ligeramente diferente. La gran diferencia es que cuando pasa argumentos, se llama al método __call__ una vez en __init__ y se supone que devuelve una función que se llamará siempre que se llame a la función decorada. Cuando no hay argumentos, se llama al método __call__ cada vez que se llama a la función decorada.

¿Qué significa esto para usted? La forma en que __init__ y __call__ se llaman para un @no_arg_decorator es diferente de lo que se les llama para un @decorator('with','args').

Aquí hay dos decoradores que pueden hacer el truco para usted. Puedes salir con el decorador @no_share_on (...) siempre y cuando siempre lo uses entre paréntesis.

def sharing_check(view, attr_name, request, *args, **kwargs): 
    if kwargs.get(attr_name, True): 
     from django.http import Http404 
     raise Http404('not availiable for sharing') 

    return view(request, *args, **kwargs) 

class no_share(object): 
    """A decorator w/o arguments. Usage: 
    @no_share 
    def f(request): 
     ... 
    """ 
    def __init__(self, view): 
     self.view = view 

    def __call__(self, request, *args, **kwargs): 
     return sharing_check(self.view, 'sharing', request, *args, **kwargs) 

class no_share_on(object): 
    """A decorator w/ arguments. Usage: 
    @no_share_on('something') 
    def f(request): 
     ... 
    --OR-- 
    @no_share_on() 
    def g(request): 
     ... 
    """ 
    def __init__(self, attr_name='sharing'): 
     self.attr_name = attr_name 

    def __call__(self, view): 
     def wrapper_f(request, *args, **kwargs): 
      return sharing_check(view, self.attr_name, request, *args, **kwargs) 
1

Creo que el cierre puede funcionar aquí.

def no_share(attr): 
    def _no_share(decorated): 
     def func(self, request, *args, **kwargs): 
      """Don't let them in if it's shared""" 

      if kwargs.get(attr, True): 
       from django.http import Http404 
       raise Http404('not availiable for sharing') 

      return decorated(request, *args, **kwargs) 
     return func 
    return _no_share 
0

Sé que esto ia un poco tarde .. pero yo no vi ninguna menciones de esta forma de hacer las cosas (probablemente debido a que no existía cuando se hizo la pregunta), pero, en aras de compleción Me pareció útil observar cómo los mismos Django implementaron tal cosa. Echar un vistazo a:

django.views.decorators.http.require_http_methods https://github.com/django/django/blob/master/django/views/decorators/http.py

from functools import wraps 
from django.utils.decorators import decorator_from_middleware, available_attrs 

def require_http_methods(request_method_list): 
    """ 
    Decorator to make a view only accept particular request methods. Usage:: 

    @require_http_methods(["GET", "POST"]) 
    def my_view(request): 
    # I can assume now that only GET or POST requests make it this far 
    # ... 

    Note that request methods should be in uppercase. 
    """ 
    def decorator(func): 
     @wraps(func, assigned=available_attrs(func)) 
     def inner(request, *args, **kwargs): 
      # .. do stuff here 

      return func(request, *args, **kwargs) 
     return inner 
    return decorator 
Cuestiones relacionadas