Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fixing Manually cascade delete M2M models when related model is deleted #2620

Open
wants to merge 7 commits into
base: develop
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions care/facility/models/asset.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
import uuid

from django.contrib.contenttypes.models import ContentType
from django.core.exceptions import ValidationError
from django.db import models
from django.db.models import JSONField, Q

Expand Down Expand Up @@ -54,6 +55,15 @@ class RoomType(enum.Enum):
null=True, blank=True, default=None, max_length=200
)

def delete(self, *args):
if UserDefaultAssetLocation.objects.filter(location=self).exists():
error = f"Cannot delete AssetLocation {self} because they are referenced as `location` in UserDefaultAssetLocation records."
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It might make more sense to create a static errors and make it translatable as well, also keep the errors very human readable like

raise ValidationError(error)
if FacilityDefaultAssetLocation.objects.filter(location=self).exists():
error = f"Cannot delete AssetLocation {self} because they are referenced as `location` in FacilityDefaultAssetLocation records."
raise ValidationError(error)
return super().delete(*args)


class AssetType(enum.Enum):
INTERNAL = 50
Expand Down
4 changes: 4 additions & 0 deletions care/facility/models/bed.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,10 @@ def save(self, *args, **kwargs) -> None:
return super().save(*args, **kwargs)

def delete(self, *args, **kwargs) -> None:
if ConsultationBed.objects.filter(bed=self).exists():
error = f"Cannot delete Bed {self} because they are referenced as `bed` in ConsultationBed records."
raise ValidationError(error)

DraKen0009 marked this conversation as resolved.
Show resolved Hide resolved
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shouldn't we actually delete this ? ie call the super delete method ?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My bad, didnt see the old bits !

AssetBed.objects.filter(bed=self).update(deleted=True)
super().delete(*args, **kwargs)

Expand Down
15 changes: 14 additions & 1 deletion care/facility/models/facility.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
from django.conf import settings
from django.contrib.auth import get_user_model
from django.contrib.postgres.fields import ArrayField
from django.core.exceptions import ValidationError
from django.core.validators import MinValueValidator
from django.db import models, transaction
from django.db.models import IntegerChoices
Expand Down Expand Up @@ -302,14 +303,26 @@ def save(self, *args, **kwargs) -> None:

@transaction.atomic
def delete(self, *args):
from care.facility.models.asset import Asset, AssetLocation
from care.facility.models.asset import (
Asset,
AssetLocation,
FacilityDefaultAssetLocation,
)
from care.facility.models.patient_sample import PatientSample
DraKen0009 marked this conversation as resolved.
Show resolved Hide resolved

if FacilityDefaultAssetLocation.objects.filter(facility=self).exists():
error = f"Cannot delete Facility {self} because they are referenced as `facility` in FacilityDefaultAssetLocation records."
raise ValidationError(error)
AssetLocation.objects.filter(facility_id=self.id).update(deleted=True)
Asset.objects.filter(
current_location_id__in=AssetLocation._base_manager.filter( # noqa: SLF001
facility_id=self.id
).values_list("id", flat=True)
).update(deleted=True)
FacilityUser.objects.filter(facility=self).delete()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Shifting might also be affected

PatientSample.objects.filter(testing_facility=self).update(
testing_facility=None
)
return super().delete(*args)

@property
Expand Down
9 changes: 9 additions & 0 deletions care/facility/models/patient.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

from dateutil.relativedelta import relativedelta
from django.contrib.postgres.aggregates import ArrayAgg
from django.core.exceptions import ValidationError
from django.core.validators import MaxValueValidator, MinValueValidator
from django.db import models
from django.db.models import Case, F, Func, JSONField, Value, When
Expand Down Expand Up @@ -474,6 +475,14 @@ def save(self, *args, **kwargs) -> None:
self._alias_recovery_to_recovered()
super().save(*args, **kwargs)

def delete(self, *args):
DraKen0009 marked this conversation as resolved.
Show resolved Hide resolved
from care.facility.models.patient_sample import PatientSample

if PatientSample.objects.filter(patient=self).exists():
error = f"Cannot delete PatientRegistration {self} because they are referenced as `patient` in PatientSample records."
raise ValidationError(error)
return super().delete(*args)

def get_age(self) -> str:
start = self.date_of_birth or date(self.year_of_birth, 1, 1)
end = (self.death_datetime or timezone.now()).date()
Expand Down
20 changes: 20 additions & 0 deletions care/facility/models/patient_consultation.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
from django.contrib.postgres.fields import ArrayField
from django.core.exceptions import ValidationError
from django.core.validators import MinValueValidator
from django.db import models
from django.db.models import JSONField
Expand Down Expand Up @@ -264,6 +265,25 @@ def save(self, *args, **kwargs):
self.patient.save(update_fields=["death_datetime"])
super().save(*args, **kwargs)

def delete(self, *args):
from care.facility.models.bed import ConsultationBed
from care.facility.models.patient_investigation import InvestigationValue
from care.facility.models.patient_sample import PatientSample

if InvestigationValue.objects.filter(consultation=self).exists():
error = f"Cannot delete PatientConsultation {self.external_id} because they are referenced as `consultation` in InvestigationValue records."
raise ValidationError(error)

if PatientSample.objects.filter(consultation=self).exists():
error = f"Cannot delete PatientConsultation {self} because they are referenced as `consultation` in PatientSample records."
raise ValidationError(error)

if ConsultationBed.objects.filter(consultation=self).exists():
error = f"Cannot delete PatientConsultation {self} because they are referenced as `consultation` in ConsultationBed records."
raise ValidationError(error)

return super().delete(*args)
DraKen0009 marked this conversation as resolved.
Show resolved Hide resolved

class Meta:
constraints = [
models.CheckConstraint(
Expand Down
20 changes: 20 additions & 0 deletions care/facility/models/patient_investigation.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from uuid import uuid4

from django.core.exceptions import ValidationError
from django.db import models

from care.facility.models.patient_consultation import PatientConsultation
Expand All @@ -15,6 +16,12 @@ class PatientInvestigationGroup(BaseModel):
def __str__(self) -> str:
return self.name

def delete(self, *args):
if InvestigationValue.objects.filter(group=self).exists():
error = f"Cannot delete PatientInvestigationGroup {self.name} because they are referenced as `group` in InvestigationValue records."
raise ValidationError(error)
super().delete(*args)


class PatientInvestigation(BaseModel):
name = models.CharField(max_length=500, blank=False, null=False, unique=True)
Expand All @@ -32,6 +39,13 @@ def __str__(self) -> str:
unit_part = f" in {self.unit}" if self.unit else ""
return f"{self.name}{unit_part} as {self.investigation_type}"

def delete(self, *args):
if InvestigationValue.objects.filter(investigation=self).exists():
error = f"Cannot delete PatientInvestigation {self} because they are referenced as `investigation` in InvestigationValue records."

raise ValidationError(error)
return super().delete(*args)


class InvestigationSession(BaseModel):
external_id = models.UUIDField(
Expand All @@ -41,6 +55,12 @@ class InvestigationSession(BaseModel):
User, null=False, blank=False, on_delete=models.PROTECT
)

def delete(self, *args):
if InvestigationValue.objects.filter(session=self).exists():
error = f"Cannot delete InvestigationSession {self.external_id} because they are referenced as `session` in InvestigationValue records."
raise ValidationError(error)
return super().delete(*args)

class Meta:
indexes = [
models.Index(
Expand Down
Loading