djangodjango-admindjango-managers

django soft delete doesn't cascade delete


I'm using a soft delete in my django admin, done like this. The issue is that when I delete a foreign key item, that it doesn't seem to trigger the deletes for all the items it's linked to. Or maybe it does but it's not running the custom def delete I have on my model.

-If I delete a person then they are soft-deleted, but the related account is left untouched.

-If I remove the soft deletes, then when I delete a Person, the Accounts are deleted too which is correct.

So ideally when I delete a Person I'd want it to soft delete the Person and the Accounts referencing the Person to also be soft deleted(marking them inactive).

class Person(models.Model):
    description = models.CharField(max_length=100)

    def delete(self, *args, **kwargs):
        self.active = False
        self.deleted_date = datetime.now()
        self.save()

class Account(models.Model):
    name = models.CharField(max_length=50)
    person = models.ForeignKey(Person, null=True, blank=True)
    active = models.BooleanField(default=True, editable=False)

    objects = SoftDeleteManager()

    def delete(self, *args, **kwargs):
        self.active = False
        self.deleted_date = datetime.now()
        self.save()

    def __unicode__(self):
        return "%s: %s" % (self.type,self.name)

UPDATE: I have updated the question. I had not said that I'm running a soft delete on the Person model. Also added that when the def deletes are not overridden that the cascading deletes work, but when I do override the delete, the cascading doesn't trigger.


Solution

  • Your Person model is not currently designed for "soft" deletes. When you delete a Person object, Django will remove all related Account objects. If you want to soft-delete Person objects, add a flag for that in the Person model. Then you need to make sure that your default Account manager excludes those accounts that are related to a soft-deleted Person object.

    Edit:

    1. One approach is making the default manager exclude the objects related to inactive Person objects instead of setting them "deleted":

        class AccountManager(models.Manager): 
            def get_query_set(self):
                return self.filter(person__active=True).filter(active=True)
      
    2. Another approach would be setting your related Account objects "deleted" when a Person object is soft-deleted. For that, you could use a signal. A post-save signal on Person objects would be appropriate I think.