给装饰器添加参数
我有一个装饰器,用来装饰一个Django视图,当我不想让这个视图执行时,如果share
参数为True
(这个是通过中间件处理的)
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 个回答
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
Bruce Eckel写的这篇文章,Li0liQ提到的,应该能帮助你理解这个问题。带参数和不带参数的装饰器在行为上有些不同。最大的区别在于,当你传递参数时,__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)
7
我希望布鲁斯·埃克尔的这篇文章能对你有所帮助。
更新: 根据这篇文章,你的代码应该像这样:
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, [...])