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): return self.name # Here's where to take a look def soft_delete(self): self.is_deleted = True self.deleted_at = timezone.now() self.save()
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() self.object.soft_delete() return HttpResponseRedirect(self.get_success_url())
DeleteView(), all we had to do is override the
delete() method, and replace with our
soft_delete() method defined earlier.
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?