Reputation: 30111
I have the following decorator and view which works fine.
Decorator
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
View
@event_admin_only
def event_dashboard(request, event_slug):
pass
But how can I modify my decorator such that it takes in an additional parameter like so:
@event_admin_only(obj1,[...])
def event_dashboard(request, event_slug):
pass
Upvotes: 8
Views: 10231
Reputation: 349
In case you'd like to use class based decorator:
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
Source is here.
I tried it on Django views and it worked like a charm. Moreover you do not need 3 tier nesting like in function decorators and you can accurately add some private methods in this class or do more stuff.
And then on view:
@MyDec('a and b')
def liked_news_create(request, user_id):
...
BUT NOTE (!) In debugger mode (PyCharm for example) you must use in some way the decorator_self.flag (argument supplied to your class based decorator), for example print it, or you wont see decorator_self.flag while debugging, it will say you that decorator_self is not defined. The same for functional based decorators. I stumbled on it myself.
If you want to use this decorator on class based views, then this approach is going to work (create example):
class EstimationStoreViewSet(GenericViewSet, CreateModelMixin):
permission_classes = [IsAuthenticated]
serializer_class = EstimationStoreSerializer
@MyDec('abc')
def create(self, request, *args, **kwargs):
return super().create(request, *args, **kwargs)
Upvotes: 0
Reputation: 17018
You need to wrap the decorator function creation in another function:
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
This can then be called like this:
@the_decorator("an_argument", "another_argument")
def event_dashboard(request, event_slug):
I'd recommend the answer from e-satis on this question to understand this: How to make a chain of function decorators?
Upvotes: 19