Django Rest Framework APIClient login не работает

У меня есть тестовая функция для проверки того, может ли пользователь войти в систему или нет. Я использую APIClient от DRF и вхожу в систему. Когда я делаю запрос get к защищенной конечной точке API, я получаю 401 ответ.

Я попытался напечатать:

print(client.login(username='instructor', password='asdf'))

И он возвращает True, что означает, что пользователь найден и вошел в систему.

Но все равно, когда я это делаю:

response = client.get(reverse('recommend-list'))

Я получаю 401.

Вот моя тестовая функция:

def test_user_authentication(self):
    client = APIClient()
    user_data = {
        'username': 'instructor',
        'email': 'myemail@example.com',
        'password': 'asdf'
    }
    client.post(reverse('user-list'), user_data, format='json')
    self.assertTrue(User.objects.filter(username='instructor').exists())
    print(client.login(username='instructor', password='asdf'))
    response = client.get(reverse('recommend-list'))
    self.assertEqual(response.status_code, 200)

Вот как я создаю нового пользователя:

class UserViewSet(viewsets.ViewSet):
    def create(self, request):
        serializer = UserSerializer(data=request.data)
        User = get_user_model()

        if serializer.is_valid():
            validated_data = serializer.validated_data

            try:
                user = User.objects.create_user(
                    username=validated_data['username'],
                    email=validated_data['email'],
                    password=validated_data['password']
                )

                return Response({'id': user.id, 'username': user.username}, status=status.HTTP_201_CREATED)
            except IntegrityError:
                return Response({'error': 'Username is already taken.'}, status=status.HTTP_400_BAD_REQUEST)

        else:
            return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)

Делаю запрос на получение сюда:

class RecommendViewSet(viewsets.GenericViewSet):
    authentication_classes = [BasicAuthentication]
    permission_classes = [IsAuthenticated]

    def list(self, request):
        user_id = request.user.id
        movies = recommend_movies_for_user(user_id)
        return Response({"message": movies}, status=status.HTTP_200_OK)

Правильно, я думал, что это может быть простая проблема с хэшированием, но я ошибался.

Проблема в том, что вы используете BasicAuthentication для аутентификации учетных данных, которая пытается найти имя пользователя и пароль, которые должны присутствовать в запросе headers. Таким образом, вам необходимо закодировать учетные данные и правильно задать параметры headers:

class TestUser(TestCase):

    def test_user_authentication(self):
        client = APIClient()
        user_data = {
            "username": "instructor",
            "email": "myemail@example.com",
            "password": "asdf",
        }

        client.post(reverse("user-list"), user_data, format="json")
        get_user_model().objects.get(username="instructor")

        # Encode credentials and set the header
        cstring = f"{user_data["username"]}:{user_data["password"]}".encode("utf-8")
        c=base64.b64encode(cstring)
        client.credentials(HTTP_AUTHORIZATION=f"Basic {c.decode()}")

        response = client.get(reverse("recommend-list"))
        self.assertEqual(response.status_code, 200)

С другой стороны, то, что делает .login, это присоединяет User к сессии с помощью метода authenticate. Поэтому, если вы используете SessionAuthentication, ваш оригинальный код будет работать просто отлично.

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