Search code examples
djangodjango-modelsdjango-querysetpython-3.6django-2.1

How to Raise DoesNotExist - Django SoftDelete?


I have a Abstract Model SoftDelete like follow.

class SoftDeleteManager(models.Manager):

    def get_queryset(self):
        return super().get_queryset().filter(is_deleted=False)

class SoftDeleteModel(models.Model):
     is_deleted = models.BooleanField(default=0)
     deleted_at = models.DateTimeField(null=True)

     objects = SoftDeleteManager()

     def delete(self):
         self.is_deleted = True
         self.deleted_at = timezone.now()
         self.save()

     class Meta:
        abstract = True

class Employee(SafeDeleteModel):
   pass

Whenever model gets deleted i set is_deleted to True and updating the timestamp deleted_at, and created the custom manager to override initial queryset which return only non deleted fields(is_deleted=False).

employee = Employee.objects.get(pk=1)
employee.delete()
employee.refresh_from_db() // not raising DoesNotExist

But lets say i have a Employee model which uses the SafeDeleteModel for soft delete, after deleting the model like Employee.objects.get(pk=1).delete() when i call employee.refresh_from_db(),its not raising DoesNotExist, but updates the value of is_deleted, deleted_at as expected, what mistake i made here, why its not raising DoesNotExist?


Solution

  • There's been a change in Django 2.1: refresh_from_db() now uses a model's _base_manager, not the _default_manager anymore, like for related queries. This to ensure an object can be refreshed even if it cannot be found by the default manager.

    So you should set your SoftDeleteManager as the base manager using base_manager_name. But note this comment:

    Base managers aren’t used when querying on related models. For example, if the Question model from the tutorial had a deleted field and a base manager that filters out instances with deleted=True, a queryset like Choice.objects.filter(question__name__startswith='What') would include choices related to deleted questions.

    Also I don't know how you would retrieve any object that has been deleted after you make this change, except if you make a special manager to not filter the deleted objects (e.g. deleted_objects).

    Note also that I would expect the safedelete package you referred to in your comments to have the same issue, as it's not changing the _base_manager either.