From b4f2e3232a9a80dab9aeb357e04fb3c0f07872c1 Mon Sep 17 00:00:00 2001 From: mpeltriaux Date: Fri, 10 Jul 2026 13:58:23 +0200 Subject: [PATCH] # Improvement PUT API * improves error processing and response on PUT endpoint * consolidates improved error processing into central method for all API endpoints --- api/utils/serializer/serializer.py | 12 +++++- api/views/v1/views.py | 59 ++++++++++++++---------------- 2 files changed, 38 insertions(+), 33 deletions(-) diff --git a/api/utils/serializer/serializer.py b/api/utils/serializer/serializer.py index d9b19d72..4ed514c2 100644 --- a/api/utils/serializer/serializer.py +++ b/api/utils/serializer/serializer.py @@ -6,6 +6,7 @@ Created on: 24.01.22 """ import json +import uuid from abc import abstractmethod from django.contrib.gis import geos @@ -171,13 +172,20 @@ class AbstractModelAPISerializer: Returns: """ + if not id: + raise ValueError("No id provided. The id is expected to live in the URL!") + # First if there is an external identifier linked to an internal one, so we can continue with the internal try: ext_id = ExternalIdentifier.objects.get(external_id=id) id = ext_id.internal_id except ObjectDoesNotExist: - # No external id found - let's hope the given id exists internally - pass + # Not found as external id - let's check whether this is a valid uuid and therefore + # potentially an internal id + try: + uuid.UUID(id) + except ValueError: + raise AssertionError(f"'{id}' is neither a known external identifier nor a valid uuid.") obj = self.model.objects.get( id=id, diff --git a/api/views/v1/views.py b/api/views/v1/views.py index 7b7285d9..9137b80b 100644 --- a/api/views/v1/views.py +++ b/api/views/v1/views.py @@ -10,6 +10,7 @@ from json import JSONDecodeError from django.core.exceptions import ObjectDoesNotExist from django.http import JsonResponse, HttpRequest +from django.views.decorators.csrf import csrf_exempt from api.utils.serializer.v1.compensation import CompensationAPISerializerV1 from api.utils.serializer.v1.deduction import DeductionAPISerializerV1 @@ -28,6 +29,20 @@ class AbstractAPIViewV1(AbstractAPIView): super().__init__(*args, **kwargs) self.serializer = self.serializer() + @csrf_exempt + def dispatch(self, request, *args, **kwargs): + try: + return super().dispatch(request, *args, **kwargs) + except (JSONDecodeError, + AssertionError, + ValueError, + PermissionError) as e: + return self._return_error_response(e, 400) + except ObjectDoesNotExist as e: + return self._return_error_response(e, 404) + except Exception as e: + return self._return_error_response(e, 500) + def get(self, request: HttpRequest, id=None): """ Handles the GET request @@ -40,17 +55,12 @@ class AbstractAPIViewV1(AbstractAPIView): Returns: response (JsonResponse) """ - try: - self.rpp = int(request.GET.get("rpp", self.rpp)) - self.page_number = int(request.GET.get("p", self.page_number)) - - self.serializer.rpp = self.rpp - self.serializer.page_number = self.page_number - - self.serializer.prepare_lookup(id, self.user) - data = self.serializer.fetch_and_serialize() - except Exception as e: - return self._return_error_response(e, 500) + self.rpp = int(request.GET.get("rpp", self.rpp)) + self.page_number = int(request.GET.get("p", self.page_number)) + self.serializer.rpp = self.rpp + self.serializer.page_number = self.page_number + self.serializer.prepare_lookup(id, self.user) + data = self.serializer.fetch_and_serialize() return self._return_response(request, data) def post(self, request: HttpRequest): @@ -64,16 +74,9 @@ class AbstractAPIViewV1(AbstractAPIView): Returns: response (JsonResponse) """ - try: - body = request.body.decode("utf-8") - body = json.loads(body) - created_id = self.serializer.create_model_from_json(body, self.user) - except (JSONDecodeError, - AssertionError, - ValueError, - PermissionError, - ObjectDoesNotExist) as e: - return self._return_error_response(e, 400) + body = request.body.decode("utf-8") + body = json.loads(body) + created_id = self.serializer.create_model_from_json(body, self.user) return JsonResponse({"id": created_id}) def put(self, request: HttpRequest, id=None): @@ -88,12 +91,9 @@ class AbstractAPIViewV1(AbstractAPIView): Returns: response (JsonResponse) """ - try: - body = request.body.decode("utf-8") - body = json.loads(body) - updated_id = self.serializer.update_model_from_json(id, body, self.user) - except Exception as e: - return self._return_error_response(e, 500) + body = request.body.decode("utf-8") + body = json.loads(body) + updated_id = self.serializer.update_model_from_json(id, body, self.user) return JsonResponse({"id": updated_id}) def delete(self, request: HttpRequest, id=None): @@ -107,10 +107,7 @@ class AbstractAPIViewV1(AbstractAPIView): response (JsonResponse) """ - try: - success = self.serializer.delete_entry(id, self.user) - except Exception as e: - return self._return_error_response(e, 500) + success = self.serializer.delete_entry(id, self.user) return JsonResponse( { "success": success,