From 397acd4df7f0052ad6ce75c9a5318fc86ae4e5e6 Mon Sep 17 00:00:00 2001 From: Ihsan Ullah Date: Sat, 5 Jul 2025 14:20:30 +0500 Subject: [PATCH 1/2] improved organization delete error --- src/apps/api/views/profiles.py | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/src/apps/api/views/profiles.py b/src/apps/api/views/profiles.py index 494b76a46..7c7f6dcd8 100644 --- a/src/apps/api/views/profiles.py +++ b/src/apps/api/views/profiles.py @@ -13,6 +13,7 @@ from rest_framework.response import Response from rest_framework import status from django.urls import reverse +from django.db import IntegrityError from api.permissions import IsUserAdminOrIsSelf, IsOrganizationEditor from api.serializers.profiles import MyProfileSerializer, UserSerializer, \ @@ -257,17 +258,24 @@ def delete_organization(self, request, pk=None): try: org = Organization.objects.get(id=pk) member = org.membership_set.get(user=request.user) - if member.group == Membership.OWNER: - org.delete() - return Response({ - "success": True, - "message": "Organization deleted!" - }) - else: + if member.group != Membership.OWNER: return Response({ "success": False, "message": "You do not have delete rights!" }) + + org.delete() + return Response({ + "success": True, + "message": "Organization deleted!" + }) + + except IntegrityError: + return Response({ + "success": False, + "message": "This organization cannot be deleted because it is associated with existing submissions. Please remove those submissions first." + }) + except Exception as e: return Response({ "success": False, From a5a24b737e6cdb1886539b8a3951369b4bab9cee Mon Sep 17 00:00:00 2001 From: Ihsan Ullah Date: Sun, 6 Jul 2025 14:14:55 +0500 Subject: [PATCH 2/2] remove orgnaization from submission on soft-delete --- src/apps/api/tests/test_submissions.py | 24 ++++++++++++++++++++++++ src/apps/competitions/models.py | 4 ++++ 2 files changed, 28 insertions(+) diff --git a/src/apps/api/tests/test_submissions.py b/src/apps/api/tests/test_submissions.py index fb5b3966f..a12e3ef13 100644 --- a/src/apps/api/tests/test_submissions.py +++ b/src/apps/api/tests/test_submissions.py @@ -554,6 +554,7 @@ def setUp(self): self.leaderboard = LeaderboardFactory() self.comp = CompetitionFactory(created_by=self.creator) self.phase = PhaseFactory(competition=self.comp) + self.organization = OrganizationFactory() # Approved participant CompetitionParticipantFactory(user=self.participant, competition=self.comp, status=CompetitionParticipant.APPROVED) @@ -591,6 +592,15 @@ def setUp(self): leaderboard=None ) + self.organization_submission = SubmissionFactory( + phase=self.phase, + owner=self.participant, + status=Submission.FINISHED, + is_soft_deleted=False, + leaderboard=None, + organization=self.organization + ) + def test_cannot_delete_submission_if_not_owner(self): """Ensure that a non-owner cannot soft delete a submission.""" self.client.login(username="other_user", password="other") @@ -639,3 +649,17 @@ def test_can_soft_delete_submission_successfully(self): # Refresh from DB to verify self.submission.refresh_from_db() assert self.submission.is_soft_deleted is True + + def test_organization_is_removed_from_soft_deleted_submission(self): + """Ensure a organization reference is removed from soft-deleted submission""" + self.client.login(username="participant", password="participant") + url = reverse("submission-soft-delete", args=[self.organization_submission.pk]) + resp = self.client.delete(url) + + assert resp.status_code == 200 + assert resp.data["message"] == "Submission deleted successfully" + + # Refresh from DB to verify + self.organization_submission.refresh_from_db() + assert self.organization_submission.is_soft_deleted is True + assert self.organization_submission.organization is None diff --git a/src/apps/competitions/models.py b/src/apps/competitions/models.py index d8ce83dad..97b29ef8b 100644 --- a/src/apps/competitions/models.py +++ b/src/apps/competitions/models.py @@ -576,6 +576,7 @@ def soft_delete(self): """ Soft delete the submission: remove files but keep record in DB. Also deletes associated SubmissionDetails and cleans up storage. + Also removes organization reference from the submission """ # Remove related files from storage @@ -600,6 +601,9 @@ def soft_delete(self): # Clear the data field for this submission self.data = None + # Clear the organization field for this submission + self.organization = None + # Mark submission as deleted self.is_soft_deleted = True self.soft_deleted_when = now()