0

I wonder why tokens are not generated in my Django project. It seems to me that everything worked properly but now I get:

django_1   | Traceback (most recent call last):
django_1   |   File "/usr/local/lib/python3.6/site-packages/django/core/handlers/exception.py", line 41, in inner
django_1   |     response = get_response(request)
django_1   |   File "/usr/local/lib/python3.6/site-packages/django/core/handlers/base.py", line 187, in _get_response
django_1   |     response = self.process_exception_by_middleware(e, request)
django_1   |   File "/usr/local/lib/python3.6/site-packages/django/core/handlers/base.py", line 185, in _get_response
django_1   |     response = wrapped_callback(request, *callback_args, **callback_kwargs)
django_1   |   File "/usr/local/lib/python3.6/site-packages/django/views/decorators/csrf.py", line 58, in wrapped_view
django_1   |     return view_func(*args, **kwargs)
django_1   |   File "/usr/local/lib/python3.6/site-packages/django/views/generic/base.py", line 68, in view
django_1   |     return self.dispatch(request, *args, **kwargs)
django_1   |   File "/usr/local/lib/python3.6/site-packages/rest_framework/views.py", line 489, in dispatch
django_1   |     response = self.handle_exception(exc)
django_1   |   File "/usr/local/lib/python3.6/site-packages/rest_framework/views.py", line 449, in handle_exception
django_1   |     self.raise_uncaught_exception(exc)
django_1   |   File "/usr/local/lib/python3.6/site-packages/rest_framework/views.py", line 486, in dispatch
django_1   |     response = handler(request, *args, **kwargs)
django_1   |   File "/code/myProject/backend/myProject/companies/views.py", line 43, in post
django_1   |     return Response({'token': user.auth_token.key, 'id': user.id}, status=status.HTTP_200_OK)
django_1   |   File "/usr/local/lib/python3.6/site-packages/django/db/models/fields/related_descriptors.py", line 404, in __get__
django_1   |     self.related.get_accessor_name()
django_1   | django.db.models.fields.related_descriptors.RelatedObjectDoesNotExist: User has no auth_token.

I checked my database and there is no token. Obviously I can add it through admin panel and then it works, but of course it should be generated automatically when user sign in. Any ideas why it's not working?

url(r'^login', views.UserAuthenticationView.as_view()),

views.py:

@permission_classes([CustomPermission])
class UserAuthenticationView(GenericAPIView):

    serializer_class = UserAuthenticationSerializer

    def post(self, request, *args, **kwargs):
        serializer = UserAuthenticationSerializer(data=self.request.data)
        if serializer.is_valid():
            user = serializer.validated_data['user']
            return Response({'token': user.auth_token.key, 'id': user.id}, status=status.HTTP_200_OK)
        return Response(serializer.errors, status=status.HTTP_401_UNAUTHORIZED)

serializers.py:

class UserAuthenticationSerializer(serializers.Serializer):
    username = serializers.CharField()
    password = serializers.CharField()

    def validate(self, attrs):
        username = attrs.get('username')
        password = attrs.get('password')

        if username and password:
            user = authenticate(username=username, password=password)
            if user:

                if not user.is_active:
                    msg = 'User account is disabled.'
                    raise serializers.ValidationError(msg, code='authorization')

            else:
                msg = 'Unable to log in with provided credentials.'
                raise serializers.ValidationError(msg, code='authorization')

        else:
            msg = 'Must include "username" and "password".'
            raise serializers.ValidationError(msg, code='authorization')

        attrs['user'] = user
        return attrs

1 Answers1

0

You should get or create the Token manually.

The get_or_create method fits perfectly:

from rest_framework.authtoken.models import Token


@permission_classes([CustomPermission])
class UserAuthenticationView(GenericAPIView):

    serializer_class = UserAuthenticationSerializer

    def post(self, request, *args, **kwargs):
        serializer = self.serializer_class(data=request.data)
        serializer.is_valid(raise_exception=True)

        user = serializer.validated_data['user']
        token, created = Token.objects.get_or_create(user=user)

        response_data = {
            'id': user.id,
            'token': token.key
        }

        return Response(response_data, status=status.HTTP_200_OK)

Now the Token will be generated if it doesn't exist yet, or will be taken from the database if it is already created.

Note: You can pass the raise_exception=True argument to is_valid() in order not to write this if statement.

wencakisa
  • 5,850
  • 2
  • 15
  • 36
  • @Wahtdbogh The default `TokenAuthentication` does not support expiring. But you can have a look at this resource: https://stackoverflow.com/questions/14567586/token-authentication-for-restful-api-should-the-token-be-periodically-changed – wencakisa Aug 22 '17 at 13:12
  • @Wahtdbogh I've just found this package: https://github.com/JamesRitchie/django-rest-framework-expiring-tokens It seems very easy to use, have a look at it too. – wencakisa Aug 22 '17 at 13:13
  • Yes, I found it, too. I just wonder whether it is necessary to use it? –  Aug 22 '17 at 13:15
  • It's up to you whether you want to have this feature or not. I maintain a project that uses `TokenAuthentication` and tokens *do not* have expiry. And I am okay with that. It's all your choice. – wencakisa Aug 22 '17 at 13:18