Soft Delete in Django Quickly

Adrienne Domingus has a beautiful article on soft delete in Django, taking the custom model manager approach. (Article here). Let’s see another quick way to throw in soft delete in your Django application. It might not be the cleanest or best approach. However, it works well for my little setup.

What is Soft Delete?

Soft delete simply refers to giving the user an impression their item (or object) is deleted from the database, except only a flip was set on the object to hide it from usual view.

Therefore, if a user deletes their blog post, instead of truly, literally deleting the post from the database (which is truly, literally) irreversible, the developers simply sets a flag on the object, such as is_deleted=True, hiding the object from the usual running of the site.

Why am I even explaining what soft delete is?

A possible approach

Again, this is simply one way of going about it, which might not be the best approach for your use case. It is a free world; use what works best for you. In my case, I didn’t have to do lots of model level plumbings to get soft deletes working.

Here’s what I did.

class Package(TimeStampedModel):
  # lots of other fields here, maybe
  name = models.CharField(max_length=200)
  is_deleted = models.BooleanField(default=False)
  deleted_at = models.DateTimeField(blank=True, null=True)

  def __str__(self):

  # Here's where to take a look
  def soft_delete(self):
    self.is_deleted = True
    self.deleted_at =

Nothing fancy happening here. We’ve defined a new method on our model class, namely, soft_delete() which will simply flip add some values for us.

How do we put to use?

class PackageDelete(DeleteView):
  model = Package
  success_url = reverse_lazy('package_list')
  template_name = 'package/delete.html'

  def delete(self, request, *args, **kwargs):
    self.object = self.get_object()
    return HttpResponseRedirect(self.get_success_url())

Using CBV’s DeleteView(), all we had to do is override the delete() method, and replace with our soft_delete() method defined earlier.

Pretty straightforward.

How to Query?

To keep all is_deletedflagged objects from our usual operations and query, be specific with your queries via filters.

class PackageList(ListView):
  template_name = 'package/list.html'
  paginate_by = 25
  queryset = Package.objects.filter(is_deleted=False).order_by('-createdAt');

Then in our views, we use the is_deletedfiltering to only select the none deleted objects.


Remember that, the above approach takes the opposite approach to what Adrienne’s article suggests. This approach maintains the existing functionality of Django’s model delete procedure (which is 100% permanent delete), while Adrienne’s replaces the default permanent delete of Django’s model with a soft delete version, then providing a hard_delete() method for the permanent deletion.

If your use case would mean flipping the soft delete feature app-wide in your Django project, a custom Model Manager might be useful.

For simple use cases, my approach might be useful and lean.

Hope you enjoyed this one, see you in the next one.

PS: Shouldn’t this (soft deletion thing) be a default feature in Django, by the way?


Related Articles

Check Also
Back to top button