Django Soft-Delete
Soft delete" in database lingo means that you set a flag on an existing table which indicates that a record has been deleted, instead of actually deleting the record.
Let's say you want to implement soft-delete for a model defined in a django application. Let's imagine you have a model Item:
from django.db import models
class Item(models.Model):
title = models.CharField(max_length=255)
content = models.TextField()Now you want to add ability to use soft delete for this model. First we need to add a field that will tell us if our Item is deleted. We have at least 2 options here: we can add a boolean is_deleted field or a datetime deleted_at field. We will use the latter in this example. Let's create an abstract model for simplicity:
from django.db import models
from django.utils import timezone
class SoftDeleteModel(models.Model):
deleted_at = models.DateTimeField(blank=True, null=True)
def delete(self, *args, **kwargs):
self.deleted_at = timezone.now()
self.save()
def restore(self):
self.deleted_at = None
self.save()
def hard_delete(self, *args, **kwargs):
super(SoftDeleteModel, self).delete(*args, **kwargs)
class Meta:
abstract = True
Next, we need to implement a custom queryset object, so we can do same operations with querysets:
from django.utils import timezone
from django.db.models.query import QuerySet
class SoftDeleteQuerySet(QuerySet):
def delete(self):
return super(SoftDeleteQuerySet, self).update(deleted_at=timezone.now())
def restore(self):
return super(SoftDeleteQuerySet, self).update(deleted_at=None)
def hard_delete(self):
return super(SoftDeleteQuerySet, self).delete()
def not_deleted(self):
return self.filter(deleted_at=None)
def deleted(self):
return self.exclude(deleted_at=None)
Quite easy. Now we need to define a custom object manager and use queryset that we created in previous code block:
from django.db import models
from softdelete.querysets import SoftDeleteQuerySet
class SoftDeleteManager(models.Manager):
def __init__(self, *args, **kwargs):
self.all_objects = kwargs.pop('all_objects', False)
super(SoftDeleteManager, self).__init__(*args, **kwargs)
def get_queryset(self):
if self.all_objects:
return SoftDeleteQuerySet(self.model)
return SoftDeleteQuerySet(self.model).not_deleted()
def hard_delete(self):
return self.get_queryset().hard_delete()As a final change we just change our Item model so it inherits from SoftDeleteModel, and it's done!
from softdelete.models import SoftDeleteModel
class BlogPost(SoftDeleteModel):
title = models.CharField(max_length=255)
content = models.TextField()