Can I prevent Django from deleting an object depending on an attribute in a referencing type?

Issue

Imagine PetOwner and a Pet models:

class PetOwner(models.Model):
  name = models.CharField()

class Pet(models.Model):
  owner = models.ForeignKey('PetOwner', on_delete=models.CASCADE)
  alive = models.BooleanField(default=True)

I would like it to be possible to delete PetOwners, but only if all theiry associated Pets are not alive anymore. If that is the case, the PetOwner can be deleted and all his associated pets are also deleted (cascade semantics).

Is that possible?

Solution

You are talking about to set a ‘business rule’. You can write your ‘business rules’ in several places, for example, in each view or process that delete PetOwners.

Also, you can override delete method on model. Take in mind:

Overridden model methods are not called on bulk operations

Note that the delete() method for an object is not necessarily called when deleting objects in bulk using a QuerySet or as a result of a cascading delete. To ensure customized delete logic gets executed, you can use pre_delete and/or post_delete signals.

This is how to override delete:

from django.core.exceptions import PermissionDenied

class PetOwner(models.Model):

  # ...

  def delete(self):
    has_pets_alive = self.pet_set.filter(alive=True).exists()
    if has_pets_alive:
        raise PermissionDenied("This owner has pets alive")        
    super(PetOwner, self).delete()

Another solution are signals:

from django.db.models.signals import pre_delete 
from django.dispatch import receiver
    
@receiver(pre_delete, sender=PetOwner)
def check_pets_alive(sender, instance, **kwargs):
    has_pets_alive = instance.pet_set.filter(alive=True).exists()
    if has_pets_alive:
        raise PermissionDenied("This owner has pets alive")  

Answered By – dani herrera

This Answer collected from stackoverflow, is licensed under cc by-sa 2.5 , cc by-sa 3.0 and cc by-sa 4.0

Leave a Reply

(*) Required, Your email will not be published