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
использует эту настройку для связи токена с пользователем.