2

Im using get_queryset, in ListAPIView

I want to check the user's access token, before providing the list, I done the below but the issue is that get_query set does not return a Response, is there a way to return a response, or I should use an alternative :

this my class in the views.py :

class ListProductsOfCategory(generics.ListAPIView):
    serializer_class = ProductSerializer
    lookup_url_kwarg = 'category_id'

    def get_queryset(self):
        # I get the token here from the headers 
        token = self.request.META.get("HTTP_TOKEN", "")
        if not token:
            return Response(
                data={
                    "message": "no token!"
                },
                status=status.HTTP_400_BAD_REQUEST
            )
        if not UserAccess.objects.filter(accessToken=token).exists():
            return Response(
                data={
                    "message": "invalid token!"
                },    
                status=status.HTTP_400_BAD_REQUEST
            )
        category_id = self.kwargs.get(self.lookup_url_kwarg)
        return Product.objects.filter(category_id=category_id)

note that everything is working perfect If I removed the token related part.

thanks in advance.

after last update this is the repsonse :

enter image description here

MhmdRizk
  • 1,591
  • 2
  • 18
  • 34

2 Answers2

6

I'd suggest you to move check token logic into dispatch() method. It's a better place than get_queryset. Or even better to write your own authentication class in order to share it between views.

With some fixes (see updated get_queryset()) it can be:

UPDATE

I think you can go with built-in restframework.exceptions.AuthenticationFailed. If you are not satisfied with default DRF exceptions you can create your own custom exceptions. For example somewhere in exceptions.py:

from rest_framework.exceptions import APIException

class MissingTokenException(APIException):
    status_code = 400
    default_detail = 'Your request does not contain token'
    default_code = 'no_token'

class InvalidTokenException(APIException):
    status_code = 400
    default_detail = 'Your request contain invalid token'
    default_code = 'invalid_token'

Then you can use them in views.py:

from rest_framework import serializers
from .exceptions import MissingTokenException, InvalidTokenException

class ListProductsOfCategory(generics.ListAPIView):
    serializer_class = ProductSerializer
    lookup_url_kwarg = 'category_id'

    def dispatch(self, *args, **kwargs):
        token = self.request.META.get("HTTP_TOKEN", "")
        if not token:
            raise MissingTokenException
        if not UserAccess.objects.filter(accessToken=token).exists():
            raise InvalidTokenException
        return super().dispatch(*args, **kwargs)

    def get_queryset(self):
        qs = super().get_queryset()
        category_id = self.kwargs.get(self.lookup_url_kwarg)
        return qs.filter(category_id=category_id)
Satevg
  • 1,601
  • 2
  • 16
  • 23
  • this seems to work I only have one issue, that I dont want to raise a validation error 500 , I want o raise a cutom error, can you help please ? – MhmdRizk Nov 02 '18 at 12:29
  • @MhmdRizk I have updated my answer with custom errors example. Btw Validation error returns 400 code, as in your question's code. See link to drf exceptions doc. – Satevg Nov 02 '18 at 12:51
  • I added the response I'm getting when trying your answer – MhmdRizk Nov 02 '18 at 13:23
  • @MhmdRizk you need to add `Content-Type: application/json` header in your postman request. Then you should get json response. See https://stackoverflow.com/questions/31108075/django-rest-framework-accept-json-data/31121191#31121191 for reference – Satevg Nov 02 '18 at 13:31
  • I tried adding this, and also tried Content-Type : application/x-www-form-urlencoded, but it didnt work – MhmdRizk Nov 02 '18 at 13:44
  • @MhmdRizk Do you expect to get json response? Like `{'invalid_token': 'Your request contain invalid token'}` or something else ? Then sorry, you need `Accept:application/json` header – Satevg Nov 02 '18 at 13:47
0

I'm not 100% if I'm getting this right, but I believe you can just use the regular authentication mechanisms that DRF provides. In this particular example, I think this section of the DRF docs should show you how to do it the "DRF" way: Setting Authentication Scheme

If you add the TokenAuthentication scheme to your application, you don't need to verify the token in your get_queryset method, but you can just use decorators to restrict access for function-based views or permission_classes for class-based views:

View-based

I guess this is what you'd be most interested in.

class ListProductsOfCategory(generics.ListAPIView):
    serializer_class = ProductSerializer
    lookup_url_kwarg = 'category_id'

    authentication_classes = (TokenAuthentication, ) # Add others if desired
    permission_classes = (IsAuthenticated,)

Route-based

If you only want to restrict access for some of your routes (e.g. only post or detail views), then you can write your own permission class. For example, see this question here: How to add django rest framework permissions on specific method only ?

wuser92
  • 479
  • 3
  • 16