Django: ViewSets not accepting login required, rending SessionAuthentication useless

Viewed 867

I am having issues using the DRF SessionAuthentication with views defined as ModelViewSets. The "LoginRequiredMixin" works fine in generics views, but I really don't like those as they require me to define all the urls manually and set them in the proper order. Very tedious when ViewSets allow you to define routes way more neatly.

I have tried using the "@login_required" decorator following Django's doc authentication login(), but it doesn't accept it. Have tried specifying the authentication_class SessionAuthentication in my ViewSet but to no avail, following those posts:

Current FooBar_views.py file. The only way I have here so far to make sure a user is authenticated, is to check his JWT provided in the headers of his http request.

foobar_vews.py with ViewSets

# @login_required not being accepted when imported
class FooBarViewSet(viewsets.ModelViewSet):
    """
    Foo Bar ViewSet calling various serializers depending on request type (GET, PUT etc.)
    """

    # Trying to filter FooBars list by status: Exluding unwanted ones
    queryset = FooBar.objects.exclude(status__name = 'SOLD').exclude(status__name = 'DELETED').order_by('id').reverse()

    # mapping serializer into the action
    serializer_classes = {
        'list': FooBarIndexSerializer,
        'retrieve': FooBarDetailsSerializer,
        'create': FooBarCreateSerializer,
    }

    # Your default serializer
    default_serializer_class = FooBarIndexSerializer

    def get_serializer_class(self):
        """
        Method to detect request type (GET, PUT etc.) and select corresponding serializer.
        """
        return self.serializer_classes.get(self.action, self.default_serializer_class)

    # TODO: Move this to the permissions.py file?
    def get_permissions(self):
        """
        Method to apply permissions depending on request type (GET, PUT etc.)
        """
        if self.request.method == 'GET':
            return [permissions.DjangoModelPermissionsOrAnonReadOnly()]
        elif self.request.method == 'POST':
            return [permissions.IsAuthenticated(), IsArtist()]
        else:  # PUT, PATCH, DELETE
            return [permissions.IsAuthenticated(), IsOwner()]

Equivalent I'd need to define if using generics views. As can be seen, I don't need to add any "isAuthenticated" permission class as the "LoginRequiredMixin" checks that the user is logged, by checking the sessionId and CSRFToken.

Problem here, I need to manually define all the corresponding urls in the url.py file. Which is tedious and gets confusing as the FooBarListView and FooBarCreateView should normally be the same url, but different requests surch as GET and POST.

foobar_vews.py with generic views

class FooBarListView(LoginRequiredMixin, ListAPIView):
   queryset = FooBar.objects.all()
   serializer_class = FooBarIndexSerializer

class FooBarDetailView(LoginRequiredMixin, RetrieveAPIView):
   queryset = FooBar.objects.all()
   serializer_class = FooBarDetailsSerializer

# TODO: Voir pour passer aux modelViews
class FooBarCreateView(LoginRequiredMixin, CreateAPIView):
   queryset = FooBar.objects.all()
   serializer_class = FooBarCreateSerializer
   permission_classes = [IsOwner]

   def perform_create(self, serializer):
       user=self.request.user
       serializer.save(user=user)

class FooBarUpdateDestroyView(LoginRequiredMixin, UpdateDestroyAPIView):
   queryset=FooBar.objects.all()
   serializer_class = FooBarDetailsSerializer
   permission_classes = [IsOwner]

Therefore, is there a way to check a user is looged in ViewSets or am I forced to use a JWT authentication system, save the tokens client-side (is that even safe?), and from the front-end make sure that for every request the user provides those in the headers?

Any help on how to implement this in ViewSets would be appreciated.

1 Answers

First thing: You have to understand that Decorators can only be used on functions, you can not use it in class based views the way you have used it. So its obvious that django viewsets will not accept the @login_required decorator.

So question is, can you decorate the methods of a class? the answer is, YES. Here is the django docs Decorating the class, its says,

To decorate every instance of a class-based view, you need to decorate the class definition itself. To do this you apply the decorator to the dispatch() method of the class.

A method on a class isn’t quite the same as a standalone function, so you can’t just apply a function decorator to the method – you need to transform it into a method decorator first. The method_decorator decorator transforms a function decorator into a method decorator so that it can be used on an instance method. For example:

class ProtectedView(TemplateView):
    template_name = 'secret.html'

    @method_decorator(login_required)
    def dispatch(self, *args, **kwargs):
        return super().dispatch(*args, **kwargs)
Related