feat(src/dashboard/board_views): add atomic transaction and select_for_update to move_ticket view
Ensure consistent status updates and prevent race conditions when moving tickets
This commit is contained in:
3 files changed
+21
-18
No files matched your search
@@ -2,6 +2,7 @@ import json
|
||||
|
||||
from django.contrib import messages
|
||||
from django.contrib.auth import get_user_model
|
||||
from django.db import transaction
|
||||
from django.http import JsonResponse
|
||||
from django.shortcuts import get_object_or_404, redirect
|
||||
from django.views.decorators.http import require_http_methods
|
||||
@@ -48,6 +49,11 @@ def board_create_ticket(request, project_id):
|
||||
due_date=due_date,
|
||||
created_by=request.user,
|
||||
)
|
||||
ticket = (
|
||||
Ticket.objects.select_related("project", "created_by")
|
||||
.prefetch_related("assignees")
|
||||
.get(pk=ticket.pk)
|
||||
)
|
||||
|
||||
for uid in assignee_ids:
|
||||
TicketUser.objects.get_or_create(ticket=ticket, user_id=uid)
|
||||
@@ -89,7 +95,14 @@ def board_create_ticket(request, project_id):
|
||||
|
||||
@require_http_methods(["POST"])
|
||||
def move_ticket(request, project_id, ticket_id):
|
||||
ticket = get_object_or_404(Ticket, pk=ticket_id, project_id=project_id)
|
||||
with transaction.atomic():
|
||||
ticket = get_object_or_404(
|
||||
Ticket.objects.select_for_update()
|
||||
.select_related("project", "created_by")
|
||||
.prefetch_related("assignees"),
|
||||
pk=ticket_id,
|
||||
project_id=project_id,
|
||||
)
|
||||
data = json.loads(request.body)
|
||||
status_id = data.get("status_id")
|
||||
if not status_id:
|
||||
@@ -128,7 +141,6 @@ def move_ticket(request, project_id, ticket_id):
|
||||
message=f"Ticket moved to {status.name} by {request.user.get_full_name() or request.user.username}",
|
||||
data={"ticket_id": ticket.id, "ticket_uuid": ticket.uuid},
|
||||
)
|
||||
# Send email notification
|
||||
send_notification_email(
|
||||
user=u,
|
||||
notification_type="status_changed",
|
||||
|
||||
@@ -47,9 +47,7 @@ def project_board(request, project_id=None):
|
||||
pk=project_id,
|
||||
)
|
||||
project_members = selected_project.project_members.select_related("user").all()
|
||||
statuses = TicketStatus.objects.filter(project=selected_project).order_by(
|
||||
"sort_order"
|
||||
)
|
||||
statuses = list(selected_project.ticket_statuses.order_by("sort_order"))
|
||||
|
||||
tickets_qs = (
|
||||
Ticket.objects.filter(project=selected_project)
|
||||
@@ -132,25 +130,18 @@ def project_board(request, project_id=None):
|
||||
first_status_name = None
|
||||
statuses_json = "[]"
|
||||
if selected_project:
|
||||
first_status = (
|
||||
TicketStatus.objects.filter(project=selected_project)
|
||||
.order_by("sort_order")
|
||||
.first()
|
||||
)
|
||||
first_status = statuses[0] if statuses else None
|
||||
if first_status:
|
||||
first_status_id = first_status.id
|
||||
first_status_name = first_status.name
|
||||
statuses_json = json.dumps(
|
||||
[
|
||||
{"id": s.id, "name": s.name, "color": s.color}
|
||||
for s in TicketStatus.objects.filter(project=selected_project).order_by(
|
||||
"sort_order"
|
||||
)
|
||||
]
|
||||
[{"id": s.id, "name": s.name, "color": s.color} for s in statuses]
|
||||
)
|
||||
|
||||
permissions = (
|
||||
BoardRolePermission.objects.filter(project=selected_project).order_by("role")
|
||||
BoardRolePermission.objects.filter(project=selected_project)
|
||||
.prefetch_related("move_to_statuses")
|
||||
.order_by("role")
|
||||
if selected_project
|
||||
else []
|
||||
)
|
||||
|
||||
@@ -25,7 +25,7 @@ def export_board_tickets(request, project_id):
|
||||
|
||||
tickets = (
|
||||
Ticket.objects.filter(project=project)
|
||||
.select_related("status", "priority", "epic", "created_by")
|
||||
.select_related("status", "priority", "epic", "created_by", "project")
|
||||
.prefetch_related("assignees")
|
||||
.order_by("-created_at")
|
||||
)
|
||||
|
||||
Reference in new issue
Block a user