lbris
lbris

Reputation: 1249

DRF : parameter in custom permission class with function based views

I use Django Rest Framework to build my API and it works fine. I use function based views to manage my endpoints. It works well too but I'm trying to add a custom permission class and putting parameters with it do not work.

An example of one of my endpoints :

@api_view(http_method_names=["GET"])
@permission_classes([IsAuthenticated])
def team_kanban(request):
    """
    List all Helpdesk Teams with kanban fields.
    """
    teams = HelpdeskTeam.objects.all()
    return Response(HelpdeskTeamKanbanSerializer(teams, many=True).data)

It works without any problem. But when I try to do :

@api_view(http_method_names=["GET"])
@permission_classes([IsAuthenticated, HasPermission("view_helpdeskteam")])
def team_kanban(request):
    """
    List all Helpdesk Teams with kanban fields.
    """
    teams = HelpdeskTeam.objects.all()
    return Response(HelpdeskTeamKanbanSerializer(teams, many=True).data)

with this custom permission class :

from rest_framework import permissions


class HasPermission(permissions.BasePermission):
    """
    Allows access only to users who have the appropriate permission.
    """

    permission_codename = ""

    def __init__(self, permission_codename):
        super().__init__()
        self.permission_codename = permission_codename

    def has_permission(self, request, view):
        return request.user.has_permission(self.permission_codename)

It doesn't work. The full error is :

Traceback (most recent call last):
  File "C:\Program Files\Python37\lib\site-packages\django\core\handlers\exception.py", line 47, in inner
    response = get_response(request)
  File "C:\Program Files\Python37\lib\site-packages\django\core\handlers\base.py", line 181, in _get_response
    response = wrapped_callback(request, *callback_args, **callback_kwargs)
  File "C:\Program Files\Python37\lib\site-packages\django\views\decorators\csrf.py", line 54, in wrapped_view
    return view_func(*args, **kwargs)
  File "C:\Program Files\Python37\lib\site-packages\django\views\generic\base.py", line 70, in view
    return self.dispatch(request, *args, **kwargs)
  File "C:\Program Files\Python37\lib\site-packages\rest_framework\views.py", line 509, in dispatch
    response = self.handle_exception(exc)
  File "C:\Program Files\Python37\lib\site-packages\rest_framework\views.py", line 469, in handle_exception
    self.raise_uncaught_exception(exc)
  File "C:\Program Files\Python37\lib\site-packages\rest_framework\views.py", line 480, in raise_uncaught_exception
    raise exc
  File "C:\Program Files\Python37\lib\site-packages\rest_framework\views.py", line 497, in dispatch
    self.initial(request, *args, **kwargs)
  File "C:\Program Files\Python37\lib\site-packages\rest_framework\views.py", line 415, in initial
    self.check_permissions(request)
  File "C:\Program Files\Python37\lib\site-packages\rest_framework\views.py", line 331, in check_permissions
    for permission in self.get_permissions():
  File "C:\Program Files\Python37\lib\site-packages\rest_framework\views.py", line 278, in get_permissions
    return [permission() for permission in self.permission_classes]    
  File "C:\Program Files\Python37\lib\site-packages\rest_framework\views.py", line 278, in <listcomp>
    return [permission() for permission in self.permission_classes]    
TypeError: 'HasPermission' object is not callable

It seems my class is not callable with the parameter.

How can I achieve this ?

Thanks in advance :)

Upvotes: 3

Views: 2311

Answers (1)

Iain Shelvington
Iain Shelvington

Reputation: 32304

permission_classes must be passed a list of permission classes, not instances of permissions. One way to dynamically create permission classes would be to create a method that returns a dynamically created class using type

from rest_framework import permissions


class HasPermissionBase(permissions.BasePermission):
    """
    Allows access only to users who have the appropriate permission.
    """

    permission_codename = ""

    def has_permission(self, request, view):
        return request.user.has_permission(self.permission_codename)


def HasPermission(permission_codename):
    return type('HasPermission', (HasPermissionBase, ), {'permission_codename': permission_codename})

Or you could override the __call__ method on your custom permission and return self so that it acts like a class when called



class HasPermission(permissions.BasePermission):
    """
    Allows access only to users who have the appropriate permission.
    """

    permission_codename = ""

    def __init__(self, permission_codename):
        super().__init__()
        self.permission_codename = permission_codename

    def __call__(self):
        return self

    def has_permission(self, request, view):
        return request.user.has_permission(self.permission_codename)

Upvotes: 6

Related Questions