Django Rest Framework TokenAuthentication не работает "Invalid Token"

Я успешно создаю токен при входе пользователя в систему (используя модель CustomUser, которая заменяет поле username на email), но при использовании этого токена в последующих запросах получаю ответ "Invalid token". Ниже приведены выдержки из соответствующих файлов:

settings.py

INSTALLED_APPS = [
    'django.contrib.admin',
    'django.contrib.auth',
    'django.contrib.contenttypes',
    'django.contrib.sessions',
    'django.contrib.messages',
    'django.contrib.staticfiles',
    'users',
    'goals',
    'rest_framework',
    'rest_framework.authtoken',
]
...

REST_FRAMEWORK = {
    'DEFAULT_AUTHENTICATION_CLASSES': [
        'rest_framework.authentication.TokenAuthentication',
    ],
}

Виды для регистрации и входа пользователей в users/views.py

class UserRegistrationView(generics.CreateAPIView):
    serializer_class = UserSerializer
    permission_classes = [permissions.AllowAny]

class UserLoginView(APIView):
    def post(self, request):
        user = authenticate(username=request.data['email'], password=request.data['password'])
        if user:
            token, created = Token.objects.get_or_create(user=user)
            return Response({'token': token.key})
        else:
            return Response({'error': 'Invalid credentials'}, status=401)

Представление, которое я тестирую в файле goals/views.py:

@api_view(['POST'])
@permission_classes((IsAuthenticated, ))
def create_topic(request):
    data = JSONParser().parse(request)
    serializer = TopicSerializer(data=data)
    if serializer.is_valid():
        serializer.save()
        return Response(serializer.data, status=201)
    else:
        return Response(serializer.errors, status=400)

Тест, который не удался в goals/tests.py

class SerializerTests(TestCase):

    def setUp(self):
        email = "normal@user.com"
        pw = "foo"
        self.user = CustomUser.objects.create_user(email=email, password=pw)
        self.user_data = {'email':email, 'password':pw}
        login_response = self.client.post('/users/login/', data=json.dumps(self.user_data), content_type="application/json")
        print(login_response.data)
        self.token = login_response.data['token']
        self.headers = {'Authorization': 'Token {self.token}'}

    def test_create_topic(self):
        self.topic = {'name':'topic_1', 'created_by':self.user.pk}
        response = self.client.post('/goals/topic/create/', data=json.dumps(self.topic), content_type="application/json", headers=self.headers) #should this be replaced by some DRF components?
        self.assertEqual(201, response.status_code)

К сожалению, response.data просто указывает {'detail': ErrorDetail(string='Invalid token.', code='authentication_failed')}. Это не похоже на проблему с заголовком, поскольку изменение формата заголовка приводит к сообщению об ошибке, в котором говорится, что не были предоставлены учетные данные для аутентификации. Таким образом, похоже, что токен отправлен успешно, но почему-то не принят. Я проверил и подтвердил, что токен, созданный при входе в систему, сохраняется должным образом, поэтому он должен быть доступен для запросов - все равно не работает...

Что я делаю не так?

Это простой вопрос.

class SerializerTests(TestCase):

    def setUp(self):
        email = "normal@user.com"
        pw = "foo"
        self.user = CustomUser.objects.create_user(email=email, password=pw)
        self.user_data = {'email':email, 'password':pw}
        login_response = self.client.post('/users/login/', data=json.dumps(self.user_data), content_type="application/json")
        print(login_response.data)
        self.token = login_response.data['token']
        self.headers = {'Authorization': 'Token {self.token}'}
        ...

Если вы посмотрите на метод SerializerTests setUp, вы устанавливаете заголовок через полученный токен.

self.headers = {'Authorization': 'Token {self.token}'}

Однако в приведенном выше предложении не применяется форматирование, поэтому self.token применяется как строка, а не как значение переменной self.token.

Для того чтобы self.token применялся в качестве значения переменной, я использовал f-string в этом коде.

self.headers = {'Authorization': f'Token {self.token}'}

Хотя ответ @Antoliny верен, этого может быть недостаточно.

В ваших настройках я не вижу настройки AUTH_USER_MODEL. Если вы используете собственную, пользовательскую модель пользователя, вы должны установить:

# settings.py

AUTH_USER_MODEL = "users.CustomUser"  # Use real path for your model.

Документы: https://docs.djangoproject.com/en/5.1/topics/auth/customizing/#substituting-a-custom-user-model

Это нужно для того, чтобы Django и все остальные пакеты воспринимали эту модель как модель по умолчанию для модели пользователя. Token модель из rest_framework.authtoken использует эту настройку для связи токена с пользователем.

Вернуться на верх