Django Soft Delete
Tags: python, django, database, programming
Published: 2023-02-07 20:42:21

Waking up to a client's mail about recovering her valuable data from a website I build for them made me write this blog.
Providing the ability to delete an object from the database is a risky feature. No matter much you warn them about the consequences of deleting their things there will be a time when they delete the data and expects you the developer to recover it.
You know the kind - "Hi Billy, I mistakenly deleted this thing which I didn't mean to and now I really need it back…Can you recover it?"

Billy:
![](https://media.giphy.com/media/52HjuHsfVO69q/giphy-downsized-large.gif)

Databases don't work that way, Jessica. When a thing is deleted, it's deleted, Finish, The End, Vanish, Khattam(The End in Hindi), Poof.

![](https://media.giphy.com/media/l0MYGj6ex4KXt5vIk/giphy.gif)

So the goal of this blog is to implement a soft delete mechanism where we will be able to define some objects to be soft-deleted for a certain time and it'll be hard-deleted after some specific time so that developers don't have to remember as they are interacting with objects which are which and be able to easily recover deleted objects for your users when they need you to.
We'll be using Python/Django as python is the most expressive language and easy to understand, But the same concept can be applied to any stack.
Here we will be building a Todo model where all the todos which are soft deleted for more than a month will be permanently deleted from the system.

```
Dependencies:
 1. Python
 2. Django
 3. Celery
```

## The Model:
Models are given the `deleted` & `delete_time` field, which lets you keep deleted objects in DB, but hides them for all practical purposes: all normal rules for cascading, etc. should be followed, except for actual removal. The admin app, however, should still be able to work with deleted objects, for the purpose of either erasing (definitely throwing them out) or restoring them. (See code below)
```python
class Todo(models.Model):
    title = models.CharField(max_length=250)
    creation_date = models.DateField(auto_now=True)
    deleted = models.BooleanField(default=False)
    delete_time = models.DateTimeField(null=True, blank=True)
```
And let's define a `soft_delete()` function for each model as follows:

```python
def soft_delete(self):
    self.deleted = True
    self.delete_time = datetime.now()
    self.save()
```

We are done with model works, now whenever the user deletes the object you can easily soft delete it like this

```python
obj.soft_delete()
```

## Task:
Now to delete an object after a certain time period you have to run a periodical task, which will gather all the todos from the model and check if the object is being deleted more than a month, for this we will have to use `django-celery-beat` for periodical tasks [Read More.](https://docs.celeryq.dev/en/latest/userguide/periodic-tasks.html#beat-custom-schedulers)
Now let's create a celery `task.py` that would run every 3–5 minutes.

```python
@periodic_task(
    run_every=(crontab(minute='*/3')),
    name="delete_old_todos",
    ignore_result=True
)
def delete_old_todos():
    todos = Todo.objects.all()
for todo in todos:
        if todo.delete:
            time_of_deletion = datetime.now() - todo.delete_time.replace(tzinfo=None)
            if time_of_deletion.days > 30:
                logger.info(" Brace yourself " + todo.title + "is getting deleted")
                todo.delete()
    return "Deleted Todos at {}".format(timezone.now())
```

All set. Now this will run in every 3 minute and will delete the object if it is more than a month old.

Find more about me:
[Twitter](https://twitter.com/subhaminion)
[LinkedIn](https://www.linkedin.com/in/subhaminion/)
[GitHub](https://github.com/subhaminion)

Cheers 🍻