【问题标题】:Adding an argument to a decorator向装饰器添加参数
【发布时间】:2010-12-23 10:56:09
【问题描述】:

我有这个装饰器,如果share 参数是True(由中间件处理),我不想执行视图时用于装饰 django 视图

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)

目前的工作方式如下:

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

但我想稍微扩展一下功能,让它像这样工作:

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

我的问题是如何修改这个装饰器类以便它接受额外的参数?

【问题讨论】:

    标签: python django decorator


    【解决方案1】:
    class no_share(object):
        def __init__(self, foo, view):
            self.foo = foo
            self.view = view
    

    【讨论】:

    • 我应该提到,我已经尝试过了,但我认为 django 的工作方式有所不同或其他原因,因为那不起作用。我试图将print "blah" 放入init 中,但是当我启动开发服务器时,我得到的只是一堆'废话'。在那之后不再有'废话'......
    • 这是预期的,init 只被调用一次,在你用它装饰的每个视图函数的定义上。每次您的服务器执行视图函数以响应请求时,都会调用 call。这个答案的方向是正确的,请参阅我的答案以获得针对您的案例的更完整的答案。
    【解决方案2】:

    我希望 Bruce Eckel 的 this 文章有所帮助。

    更新: 根据文章,您的代码将如下所示:

    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
    

    根据需要使用:

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

    【讨论】:

      【解决方案3】:

      由于您似乎在某处弄错了,这里有一个更完整的示例,可以帮助您了解自己做错了什么。将其用作插件应该可以。

      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】:

        Li0liQ 提到的 Bruce Eckel article 应该有助于解决这个问题。带参数和不带参数的装饰器的行为略有不同。最大的区别在于,当您传递参数时,__call__ 方法在 __init__ 上被调用一次,并且它应该返回一个函数,该函数将在调用装饰函数时被调用。当没有参数时,每次调用装饰函数时都会调用 __call__ 方法。

        这对您意味着什么?为 @no_arg_decorator 调用 __init__ 和 __call__ 的方式与为 @decorator('with','args') 调用的方式不同。

        这里有两个装饰器可能会为您解决问题。只要您始终将其与括号一起使用,您就可以只使用 @no_share_on(...) 装饰器。

        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)
        

        【讨论】:

          【解决方案5】:

          我认为关闭在这里可能有效。

          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
          

          【讨论】:

            【解决方案6】:

            我知道这有点晚了.. 但我没有看到任何关于这种做事方式的提及(可能是因为在提出问题时它不存在),但是,为了完整起见 我发现看看 Django 自己是如何实现这样的东西很有用。看看:

            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
            

            【讨论】:

              猜你喜欢
              • 2020-02-06
              • 1970-01-01
              • 1970-01-01
              • 2012-02-20
              • 1970-01-01
              • 2013-09-14
              • 2022-12-03
              • 2021-03-28
              • 1970-01-01
              相关资源
              最近更新 更多