Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 969
darren Avatar asked Oct 10 '22 14:10

darren


2 Answers

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.

like image 177
shanyu Avatar answered Oct 13 '22 23:10

shanyu


An alternative solution would be to use django-softdelete, a Django package that has recently appeared on github: https://github.com/scoursen/django-softdelete

If you use the supplied SoftDeleteObject mixin for your models, deleting them will automatically result in a soft deletion also of all the related models. Even better is that it stores all models related in the soft deletion, so that you can also undelete all of them with a single undelete call.

like image 31
Charl Botha Avatar answered Oct 13 '22 23:10

Charl Botha