向装饰器添加参数

时间:2009-12-05 00:03:29

标签: python django decorator

我有这个装饰器,如果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, [...])

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

6 个答案:

答案 0 :(得分:7)

我希望布鲁斯·埃克尔的this文章有所帮助。

<强> UPD: 根据文章,您的代码将如下所示:

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, [...])

答案 1 :(得分: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)

答案 2 :(得分:1)

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

答案 3 :(得分:1)

我认为封闭可能在这里有用。

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

答案 4 :(得分:0)

由于你似乎在某处弄错了,这里有一个更完整的例子,可以帮助你看到你做错了什么。使用它作为插入应该有效。

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)

答案 5 :(得分:0)

我知道这有点晚了......但我没有看到任何提及这种做事的方式(可能是因为问题被提出时它不存在),但为了完整性的利益 我发现看看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