2009-12-05 5 views
5

Je possède ce décorateur, utilisé pour décorer une vue django quand je ne veux pas la vue à exécuter si l'argument share est True (géré par middleware)Ajout d'un argument à un décorateur

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) 

Il actuellement fonctionne comme ceci:

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

Mais je suis désireux d'étendre la fonctionnalité un peu, de sorte que cela fonctionne comme ceci:

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

Ma question est comment puis-je modifier cette classe de décorateur afin qu'il accepte des arguments supplémentaires?

Répondre

7

J'espère que this article de Bruce Eckel aide.

Upd: Selon l'article de votre code ressemblera à ceci:

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 

à utiliser comme vous le souhaitez:

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

je l'ai mentionné, j'ai essayé déjà, mais je pense que django fonctionne différemment ou quelque chose parce que ne fonctionne pas .J'ai essayé de mettre "print" blah "' dans l'init, mais tout ce que j'ai eu était un tas de 'blah's' quand j'ai démarré le serveur de dev. Plus de 'bla après ça ... – priestc

+1

C'est normal, __init__ n'est appelé qu'une seule fois, à la définition de chaque fonction de vue que vous décorez avec ceci. Le __call__ est appelé chaque fois que votre serveur exécute la fonction de vue pour répondre à une demande. Cette réponse est dans la bonne direction, voir ma réponse pour une réponse plus complète spécifique à votre cas. – taleinat

0

Comme il semble que vous obtenez ce qui cloche quelque part , voici un exemple plus complet qui peut vous aider à voir ce que vous faites mal. L'utiliser comme un drop-in devrait fonctionner.

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

Bruce Eckel article que Li0liQ mentionné devrait être utile pour comprendre cela. Les décorateurs avec et sans arguments se comportent légèrement différemment. La grande différence est que lorsque vous passez des arguments, la méthode __call__ est appelée une fois sur __init__ et elle est supposée renvoyer une fonction qui sera appelée chaque fois que la fonction décorée est appelée. Lorsqu'il n'y a pas d'arguments, la méthode __call__ est appelée à chaque fois que la fonction décorée est appelée.

Qu'est-ce que cela signifie pour vous? La façon dont __init__ et __call__ sont appelés pour un @no_arg_decorator est différente de celle qui est appelée pour un @decorator('with','args').

Voici deux décorateurs qui pourraient faire l'affaire pour vous. Vous pouvez vous en passer avec le décorateur @no_share_on (...) tant que vous l'utilisez toujours avec les parenthèses.

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

Je pense que la fermeture peut fonctionner ici.

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

Je sais que ce ia un peu en retard .. mais je ne vois aucune mentions pour cette façon de faire les choses (sans doute parce qu'il n'existait pas lorsque la question a été posée), mais, dans l'intérêt de complétude J'ai trouvé utile de voir comment Django a implémenté une telle chose. Jetez un oeil à:

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 
Questions connexes