TMOTTM
TMOTTM

Reputation: 3381

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

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?

Upvotes: 0

Views: 51

Answers (1)

dani herrera
dani herrera

Reputation: 51685

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")  

Upvotes: 1

Related Questions