Django装饰器添加可选参数

8

我有以下装饰器和视图,运行正常。

装饰器

def event_admin_only(func):
    """
    Checks if the current role for the user is an Event Admin or not
    """
    def decorator(request, *args, **kwargs):
        event = get_object_or_404(Event, slug=kwargs['event_slug'])

        allowed_roles = [role[1] for role in Role.ADMIN_ROLES]

        # get user current role
        current_role = request.session.get('current_role')

        if current_role not in allowed_roles:
            url = reverse('no_perms')
            return redirect(url)
        else:       
            return func(request, *args, **kwargs)
    return decorator

查看

@event_admin_only
def event_dashboard(request, event_slug):
    pass

但是我该如何修改我的装饰器,使其接受像这样的额外参数:
@event_admin_only(obj1,[...])
def event_dashboard(request, event_slug):
    pass

2个回答

19
你需要将装饰器函数的创建包装在另一个函数中:
def the_decorator(arg1, arg2):

    def _method_wrapper(view_method):

        def _arguments_wrapper(request, *args, **kwargs) :
            """
            Wrapper with arguments to invoke the method
            """

            #do something with arg1 and arg2

            return view_method(request, *args, **kwargs)

        return _arguments_wrapper

    return _method_wrapper

然后可以这样调用:

@the_decorator("an_argument", "another_argument")
def event_dashboard(request, event_slug):

我建议阅读 e-satis 在这个问题上的回答,以理解如何制作函数装饰器链: How to make a chain of function decorators?


这段代码无法正常工作,'self'未定义,需要将其删除。 - Paul Kenjora

0
如果您想使用基于类的装饰器:
class MyDec:
    def __init__(self, flag):
        self.flag = flag

def __call__(self, original_func):
    decorator_self = self

    def wrappee(*args, **kwargs):
        print(decorator_self.flag)
        result = original_func(*args, **kwargs)
        ...
        return result

    return wrappee

源代码在这里

我在Django视图上尝试了它,效果非常好。 此外,您无需像函数装饰器中那样进行三层嵌套,而且可以在此类中准确添加一些私有方法或执行更多操作。

然后在视图上:

@MyDec('a and b')
def liked_news_create(request, user_id):
    ...

但请注意!在调试模式下(例如 PyCharm),您必须以某种方式使用 decorator_self.flag(提供给基于类的装饰器作为参数),例如打印它,否则您将无法在调试期间看到 decorator_self.flag,它会告诉您 decorator_self 未定义。对于基于函数的装饰器也是如此。我自己也遇到了这个问题。

如果您想在基于类的视图上使用此装饰器,则以下方法将起作用(创建示例):

class EstimationStoreViewSet(GenericViewSet, CreateModelMixin):
    permission_classes = [IsAuthenticated]
    serializer_class = EstimationStoreSerializer

    @MyDec('abc')
    def create(self, request, *args, **kwargs):
        return super().create(request, *args, **kwargs)

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接