2009-12-05 13 views
5

ho questo decoratore, utilizzato per decorare una vista Django quando io non voglio la vista da eseguire se l'argomento share è True (gestita dal middleware)Aggiunta di un argomento per un decoratore

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) 

Attualmente funziona così:

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

Ma sto volendo espandere la funzionalità un po ', in modo che possa funzionare in questo modo:

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

La mia domanda è come posso modificare questa classe decoratore in modo che accetti argomenti extra?

risposta

7

Spero che l'articolo this di Bruce Eckel aiuti.

Upd: Secondo l'articolo il codice sarà simile a questa:

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 

da utilizzare, se lo desideri:

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

Avrei detto, ho provato già, ma penso che Django funziona in modo diverso o qualcosa perché che non ha funzionato .Ho provato a inserire 'print" blah "' nell'iniz, ma tutto ciò che ho ottenuto è stato un po 'di bla quando ho avviato il server di sviluppo. Non più 'blah's dopo quello ... – priestc

+1

Questo è previsto, __init__ è chiamato solo una volta, sulla definizione di ogni funzione vista decorata con questo. Il __call__ viene chiamato ogni volta che il server esegue la funzione di visualizzazione per rispondere a una richiesta. Questa risposta è nella giusta direzione, vedere la mia risposta per una risposta più completa specifica per il tuo caso. – taleinat

0

Poiché sembra che stai ricevendo questo torto da qualche parte , ecco un esempio più completo che può aiutarti a capire cosa stai facendo male. Utilizzare questo come un drop-in dovrebbe funzionare.

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

The Bruce Eckel article che Li0liQ menzionato dovrebbe essere utile a capire questo fuori. I decoratori con e senza argomenti si comportano in modo leggermente diverso. La grande differenza è che quando si passano gli argomenti il ​​metodo __call__ viene chiamato una volta su __init__ e si suppone che restituisca una funzione che verrà chiamata ogni volta che viene chiamata la funzione decorata. Quando non ci sono argomenti, il metodo __call__ viene chiamato ogni volta che viene chiamata la funzione decorata.

Cosa significa questo per te? Il modo in cui __init__ e __call__ sono chiamati per un @no_arg_decorator è diverso da quello che sono chiamati per un @decorator('with','args').

Qui ci sono due decoratori che potrebbero fare il trucco per voi. Puoi farla franca solo con il decoratore @no_share_on (...) finché lo usi sempre con le parentesi.

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

Penso che la chiusura possa funzionare qui.

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

So che questo ia un po 'in ritardo .. ma non ho visto alcuna menzione di questo modo di fare le cose (probabilmente perché non esisteva quando la questione è stato chiesto), ma, nell'interesse di completezza Ho trovato utile guardare a come Django stesso ha implementato una cosa del genere. Date un'occhiata 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 
Problemi correlati