Estoy tratando de probar un punto final de API con una solicitud de parche para asegurarme de que funciona.
Estoy usando APILiveServerTestCase pero parece que no puedo obtener los permisos necesarios para parchear el elemento. Creé un usuario (usuario adminuser ) que es un superadministrador con acceso a todo y todos los permisos.
Mi caso de prueba se ve así:
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, }He probado esta prueba de varias maneras diferentes, todas dando el mismo resultado (403).
Configuré el depurador de python en la prueba, e intenté ir a http://localhost:xxxxx/admin/ en el navegador e iniciar sesión manualmente con cualquier usuario, pero la página simplemente se actualiza cuando hago clic para iniciar sesión y nunca 'iniciar sesión' para ver el administrador. No estoy seguro de si eso se debe a que no funciona completamente desde dentro de un depurador así o no.
Mi prueba se ve así (usando la biblioteca de Solicitudes):
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") Lo probé de manera muy similar a la documentación usando APIRequestFactory y forzando la autenticación:
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") Si entro en el depurador para ver las respuestas, siempre es un 403 .
El conjunto de viewset en sí es muy simple:
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 El serializador es tan básico como parece: es solo el modelo FutureVehicle y todos los campos.
Simplemente no puedo entender por qué mi usuario no inicia sesión, o si tal vez estoy haciendo algo mal en mis intentos de parchear.
Soy bastante nuevo en Django Rest Framework en general, ¡así que cualquier guía es útil!
Editar para agregar: mi configuración de DRF se ve así:
REST_FRAMEWORK = { "DEFAULT_PAGINATION_CLASS": "rest_framework.pagination.LimitOffsetPagination", "DATETIME_FORMAT": "%m/%d/%Y - %I:%M:%S %p", "DATE_INPUT_FORMATS": ["%Y-%m-%d"], "DEFAULT_AUTHENTICATION_CLASSES": [ # Enabling this it will require Django Session (Including CSRF) "rest_framework.authentication.SessionAuthentication" ], "DEFAULT_PERMISSION_CLASSES": [ # Globally only allow IsAuthenticated users access to API Endpoints "rest_framework.permissions.IsAuthenticated" ], } Estoy seguro de que el usuario adminuser es el usuario con el que deseamos iniciar sesión; si entro en el depurador y compruebo los usuarios, existen como usuarios. Durante la creación, cualquier usuario creado tiene una contraseña establecida en 'prueba'.
La prueba que ha escrito también está probando la lógica del marco de trabajo de Django (es decir, el inicio de sesión del administrador de Django). Recomiendo probar su propia funcionalidad, que ocurre después de iniciar sesión en el administrador de Django. El marco de prueba de Django ofrece un asistente para iniciar sesión en el administrador, client.login . Esto le permite concentrarse en probar su propia lógica comercial/no es necesario mantener pruebas internas de lógica comercial de autenticación de django, que pueden cambiar de versión a versión.
from django.test import TestCase, Client def TestCase(): client.login(username=self.username, password=self.password) Sin embargo, si debe replicar y administrar la lógica comercial de lo que está haciendo client.login , esta es parte de la lógica comercial de 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)Cliente de Django.inicio de sesión: https://github.com/django/django/blob/main/django/test/client.py#L596-L646