From d30cfd61c3ec0a84d89a7de42e10bf2510f36175 Mon Sep 17 00:00:00 2001 From: S4NKALP Date: Mon, 29 Jun 2026 07:20:47 +0545 Subject: [PATCH] fix(src/tickets): wrap move ticket view in atomic transaction with select_for_update Ensures data integrity and prevents race conditions when moving tickets. --- src/tickets/views.py | 41 ++++++++++++++++++++++------------------- 1 file changed, 22 insertions(+), 19 deletions(-) diff --git a/src/tickets/views.py b/src/tickets/views.py index 12693bb..7a3f426 100644 --- a/src/tickets/views.py +++ b/src/tickets/views.py @@ -1,4 +1,4 @@ -from django.db import models +from django.db import models, transaction from rest_framework import generics, permissions from rest_framework.decorators import api_view, permission_classes from rest_framework.response import Response @@ -105,26 +105,29 @@ class TicketHistoryListView(generics.ListAPIView): @api_view(["PATCH"]) @permission_classes([permissions.IsAuthenticated]) def move_ticket_view(request, pk): - ticket = generics.get_object_or_404(Ticket, pk=pk) - new_status_id = request.data.get("status_id") - if new_status_id: - new_status = generics.get_object_or_404(TicketStatus, pk=new_status_id) + with transaction.atomic(): + ticket = generics.get_object_or_404(Ticket.objects.select_for_update(), pk=pk) + new_status_id = request.data.get("status_id") + if new_status_id: + new_status = generics.get_object_or_404(TicketStatus, pk=new_status_id) - if not ticket.project.user_can_move_to_status(request.user, new_status): - return Response( - {"error": "You don't have permission to move tickets to this status."}, - status=403, - ) + if not ticket.project.user_can_move_to_status(request.user, new_status): + return Response( + { + "error": "You don't have permission to move tickets to this status." + }, + status=403, + ) - old_status_id = ticket.status_id - ticket.status_id = new_status_id - ticket.save() - if old_status_id != int(new_status_id): - TicketHistory.objects.create( - ticket=ticket, - user=request.user, - status_id=new_status_id, - ) + old_status_id = ticket.status_id + ticket.status_id = new_status_id + ticket.save() + if old_status_id != int(new_status_id): + TicketHistory.objects.create( + ticket=ticket, + user=request.user, + status_id=new_status_id, + ) return Response(TicketListSerializer(ticket).data)