Запрос на исправление не исправляется - возвращается 403 - django rest framework

Я пытаюсь протестировать конечную точку API с помощью запроса исправления, чтобы убедиться, что она работает.

Я использую APILiveServerTestCase, но не могу получить разрешения, необходимые для исправления элемента. Я создал одного пользователя (adminuser), который является суперадмином с доступом ко всему и всеми разрешениями.

Мой тестовый пример выглядит следующим образом:

class FutureVehicleURLTest(APILiveServerTestCase):
    def setUp(self):
        # Setup users and some vehicle data we can query against
        management.call_command("create_users_and_vehicle_data", verbosity=0)
        self.user = UserFactory()
        self.admin_user = User.objects.get(username="adminuser")
        self.future_vehicle = f.FutureVehicleFactory(
            user=self.user,
            last_updated_by=self.user,
        )
        self.vehicle = f.VehicleFactory(
            user=self.user,
            created_by=self.user,
            modified_by=self.user,
        )
        self.url = reverse("FutureVehicles-list")
        self.full_url = self.live_server_url + self.url
        time = str(datetime.now())
        self.form_data = {
            "signature": "TT",
            "purchasing": True,
            "confirmed_at": time,
        }

Я пробовал этот тест несколькими различными способами - все они дают один и тот же результат (403).

Я установил отладчик python в тесте, и я попытался фактически перейти на http://localhost:xxxxx/admin/ в браузере и войти в систему вручную с любым пользователем, но страница просто обновляется, когда я нажимаю на вход, и я никогда не "вхожу", чтобы увидеть администратора. Я не уверен, потому ли это, что это не полностью работает из такого отладчика или нет.

Мой тест выглядит следующим образом (с использованием библиотеки Requests):

    def test_patch_request_updates_object(self):
        data_dict = {
            "signature": "TT",
            "purchasing": "true",
            "confirmed_at": datetime.now().strftime("%m/%d/%Y, %H:%M:%S"),
        }
        url = self.full_url + str(self.future_vehicle.id) + "/"
        client = requests.Session()
        client.auth = HTTPBasicAuth(self.admin_user.username, "test")
        client.headers.update({"x-test": "true"})
        response = client.get(self.live_server_url + "/admin/")
        csrftoken = response.cookies["csrftoken"]
        # interact with the api
        response = client.patch(
            url,
            data=json.dumps(data_dict),
            cookies=response.cookies,
            headers={
                "X-Requested-With": "XMLHttpRequest",
                "X-CSRFTOKEN": csrftoken,
            },
        )
        # RESPONSE GIVES 403 PERMISSION DENIED
        fte_future_vehicle = FutureVehicle.objects.filter(
            id=self.future_vehicle.id
        ).first()
        # THIS ERRORS WITH '' not equal to 'TT'
        self.assertEqual(fte_future_vehicle.signature, "TT")

Я попробовал очень похоже на документацию , используя APIRequestFactory и принудительную аутентификацию:

    def test_patch_request_updates_object(self):
        data_dict = {
            "signature": "TT",
            "purchasing": "true",
            "confirmed_at": datetime.now().strftime("%m/%d/%Y, %H:%M:%S"),
        }
        url = self.full_url + str(self.future_vehicle.id) + "/"
        api_req_factory = APIRequestFactory()
        view = FutureVehicleViewSet.as_view({"patch": "partial_update"})
        api_request = api_req_factory.patch(
            url, json.dumps(data_dict), content_type="application/json"
        )
        force_authenticate(api_request, self.admin_user)
        response = view(api_request, pk=self.future_assignment.id)
        fte_future_assignment = FutureVehicle.objects.filter(
            id=self.future_assignment.id
        ).first()
        self.assertEqual(fte_future_assignment.signature, "TT")

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

Сам viewset очень прост:

class FutureVehicleViewSet(ModelViewSet):
    serializer_class = FutureVehicleSerializer

    def get_queryset(self):
        queryset = FutureVehicle.exclude_denied.all()
        user_id = self.request.query_params.get("user_id", None)
        if user_id:
            queryset = queryset.filter(user_id=user_id)
        return queryset

Сериализатор является базовым - это просто FutureVehicle модель и все поля.

Я просто не могу понять, почему мой пользователь не входит в систему - или, может быть, я делаю что-то неправильно в своих попытках установить патч?

Я довольно новичок в Django Rest Framework в целом, поэтому любые рекомендации будут полезны!

Рекомендуемое решение

Написанный вами тест также тестирует логику фреймворка Django (т.е. вход в админку Django). Я рекомендую тестировать вашу собственную функциональность, которая возникает после входа в админку Django. Фреймворк тестирования Django предлагает помощника для входа в админку, client.login. Это позволяет вам сосредоточиться на тестировании вашей собственной бизнес-логики/не нужно поддерживать внутренние тесты бизнес-логики аутентификации django, которые могут меняться от релиза к релизу.

from django.test import TestCase, Client


def TestCase():
   client.login(username=self.username, password=self.password)

Альтернативное решение

Однако, если вы должны воспроизвести и управлять бизнес-логикой того, что делает client.login, вот некоторая бизнес-логика из Django:

    def login(self, **credentials):
        """
        Set the Factory to appear as if it has successfully logged into a site.
        Return True if login is possible or False if the provided credentials
        are incorrect.
        """
        from django.contrib.auth import authenticate
        user = authenticate(**credentials)
        if user:
            self._login(user)
            return True
        return False

    def force_login(self, user, backend=None):
        def get_backend():
            from django.contrib.auth import load_backend
            for backend_path in settings.AUTHENTICATION_BACKENDS:
                backend = load_backend(backend_path)
                if hasattr(backend, 'get_user'):
                    return backend_path

        if backend is None:
            backend = get_backend()
        user.backend = backend
        self._login(user, backend)

    def _login(self, user, backend=None):
        from django.contrib.auth import login

        # Create a fake request to store login details.
        request = HttpRequest()
        if self.session:
            request.session = self.session
        else:
            engine = import_module(settings.SESSION_ENGINE)
            request.session = engine.SessionStore()
        login(request, user, backend)
        # Save the session values.
        request.session.save()
        # Set the cookie to represent the session.
        session_cookie = settings.SESSION_COOKIE_NAME
        self.cookies[session_cookie] = request.session.session_key
        cookie_data = {
            'max-age': None,
            'path': '/',
            'domain': settings.SESSION_COOKIE_DOMAIN,
            'secure': settings.SESSION_COOKIE_SECURE or None,
            'expires': None,
        }
        self.cookies[session_cookie].update(cookie_data)

Ссылки:

Django client.login: https://github.com/django/django/blob/main/django/test/client.py#L596-L646

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