Introduction
Soft deletion is a technique where a record is not actually removed from the database but is marked as deleted. This approach helps maintain data integrity and allows for data recovery if necessary. In this tutorial, we will walk through the steps to implement soft delete functionality in a Django application, covering model definition, view creation, template setup, and admin configuration.
Step-by-Step Guide
Step 1: Setting Up the Django Project
First, ensure you have Django installed. If not, you can install it using pip:
pip install django
Create a new Django project and app:
django-admin startproject myproject
cd myproject
django-admin startapp delete_app
Add the app to your INSTALLED_APPS
in settings.py
:
# myproject/settings.py
INSTALLED_APPS = [
...
'delete_app',
]
Step 2: Creating the Contact Model
In delete_app/models.py
, define the Contact
model with an is_deleted
field:
# delete_app/models.py
from django.db import models
class Contact(models.Model):
first_name = models.CharField(max_length=50)
last_name = models.CharField(max_length=50)
email = models.EmailField()
phone_number = models.CharField(max_length=15, blank=True, null=True)
message = models.TextField()
is_deleted = models.BooleanField(default=False)
def __str__(self):
return f"{self.first_name} {self.last_name}"
def delete(self, using=None, keep_parents=False):
self.is_deleted = True
self.save()
Run the migrations to create the table:
python manage.py makemigrations
python manage.py migrate
Step 3: Creating Views for Contact Operations
In delete_app/views.py
, implement the necessary views:
# delete_app/views.py
from django.shortcuts import render, get_object_or_404, redirect
from django.views.decorators.http import require_POST
from .models import Contact
def contact_list(request):
contacts = Contact.objects.filter(is_deleted=False)
return render(request, 'contacts/contact_list.html', {'contacts': contacts})
def contact_detail(request, pk):
contact = get_object_or_404(Contact, pk=pk)
return render(request, 'contacts/contact_detail.html', {'contact': contact})
@require_POST
def soft_delete_contact(request, pk):
contact = get_object_or_404(Contact, pk=pk)
contact.is_deleted = True
contact.save()
return redirect('delete_app:contact_list')
def create_contact(request):
if request.method == 'POST':
first_name = request.POST.get('first_name')
last_name = request.POST.get('last_name')
email = request.POST.get('email')
phone_number = request.POST.get('phone_number')
message = request.POST.get('message')
new_contact = Contact.objects.create(
first_name=first_name,
last_name=last_name,
email=email,
phone_number=phone_number,
message=message
)
return redirect('delete_app:contact_list')
return render(request, 'contacts/create_contact.html')
Step 4: Configuring URLs
In delete_app/urls.py
, define the URL patterns:
# delete_app/urls.py
from django.urls import path
from . import views
app_name = 'delete_app'
urlpatterns = [
path('contacts_list', views.contact_list, name='contact_list'),
path('',views.create_contact, name='create_contact'),
path('contact-detail/<int:pk>/', views.contact_detail, name='contact_detail'),
path('soft-delete/<int:pk>/', views.soft_delete_contact, name='soft_delete_contact'),
]
Include these URLs in the project’s main urls.py
:
# myproject/urls.py
from django.contrib import admin
from django.urls import path,include
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('delete_app.urls'))
]
Step 5: Creating Templates
Create the necessary templates in delete_app/templates/contacts/
:
contact_list.html
:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Contact List</title>
<style>
body {
font-family: 'Arial', sans-serif;
background-color: #f4f4f4;
margin: 0;
padding: 0;
}
h1 {
color: #333;
text-align: center;
padding: 20px;
background-color: #007bff;
color: #fff;
}
ul {
list-style-type: none;
padding: 0;
margin: 0;
display: flex;
flex-wrap: wrap;
justify-content: center;
}
li {
margin: 10px;
padding: 15px;
background-color: #fff;
border-radius: 5px;
box-shadow: 0 0 10px rgba(0, 0, 0, 0.1);
text-align: center;
display: flex;
flex-direction: column;
align-items: center;
}
a {
text-decoration: none;
color: #007bff;
font-weight: bold;
margin-bottom: 10px;
}
.contact-details {
font-size: 16px;
margin-top: 10px;
color: #555;
}
form {
margin-top: 10px;
}
</style>
</head>
<body>
<h1>Contact List</h1>
<ul>
{% for contact in contacts %}
<li>
<a href="{% url 'delete_app:contact_detail' pk=contact.pk %}">
{{ contact.first_name }} {{ contact.last_name }}
</a>
<div class="contact-details">
<p>Email: {{ contact.email }}</p>
<p>Phone Number: {{ contact.phone_number }}</p>
<p>Message: {{ contact.message }}</p>
</div>
<form method="post" action="{% url 'delete_app:soft_delete_contact' pk=contact.pk %}" style="display: inline;">
{% csrf_token %}
<input type="submit" value="Soft Delete">
</form>
</li>
{% endfor %}
</ul>
</body>
</html>
contact_detail.html
:
<!DOCTYPE html>
<html>
<head>
<title>Contact Detail</title>
</head>
<body>
<h1>Contact Detail</h1>
<p><strong>Name:</strong> {{ contact.first_name }} {{ contact.last_name }}</p>
<p><strong>Email:</strong> {{ contact.email }}</p>
<p><strong>Phone Number:</strong> {{ contact.phone_number }}</p>
<p><strong>Message:</strong> {{ contact.message }}</p>
<li>
<a href="{% url 'delete_app:contact_detail' pk=contact.pk %}"></a>
<form method="post" action="{% url 'delete_app:soft_delete_contact' pk=contact.pk %}" style="display: inline;">
{% csrf_token %}
<input type="submit" value="Soft Delete">
</form>
</li>
</body>
</html>
create_contact.html
:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Create a New Contact</title>
<style>
body {
font-family: 'Montserrat', sans-serif;
background-color: #f8f8f8;
margin: 0;
display: flex;
align-items: center;
justify-content: center;
min-height: 100vh;
}
.contact-form-container {
background-color: #fff;
border-radius: 10px;
box-shadow: 0 0 20px rgba(0, 0, 0, 0.1);
padding: 40px;
width: 400px;
}
.contact-form {
display: flex;
flex-direction: column;
gap: 20px;
}
.form-group {
position: relative;
}
label {
font-size: 14px;
color: #555;
position: absolute;
top: 12px;
left: 12px;
transition: all 0.3s ease;
}
input,
textarea {
padding: 16px;
border: none;
border-radius: 8px;
font-size: 16px;
background-color: #f2f2f2;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
transition: all 0.3s ease;
color: #444;
}
input:focus,
textarea:focus {
outline: none;
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2);
background-color: #e0e0e0;
}
input:not(:placeholder-shown) + label,
textarea:not(:placeholder-shown) + label {
top: -12px;
font-size: 12px;
color: #007bff;
}
button {
background-color: #007bff;
color: #fff;
padding: 16px;
border: none;
border-radius: 8px;
cursor: pointer;
font-size: 18px;
transition: background-color 0.3s ease;
}
button:hover {
background-color: #0056b3;
}
h2 {
text-align: center;
color: #007bff;
}
</style>
</head>
<body>
<div class="contact-form-container">
<h2>Create a New Contact</h2>
<form method="post" action="{% url 'delete_app:create_contact' %}" class="contact-form">
{% csrf_token %}
<div class="form-group">
<input type="text" id="first_name" name="first_name" required>
<label for="first_name">First Name</label>
</div>
<div class="form-group">
<input type="text" id="last_name" name="last_name" required>
<label for="last_name">Last Name</label>
</div>
<div class="form-group">
<input type="email" id="email" name="email" required>
<label for="email">Email</label>
</div>
<div class="form-group">
<input type="text" id="phone_number" name="phone_number">
<label for="phone_number">Phone Number</label>
</div>
<div class="form-group">
<textarea id="message" name="message" rows="4" required></textarea>
<label for="message">Message</label>
</div>
<button type="submit">Submit</button>
</form>
</div>
</body>
</html>
Step 6: Configuring the Admin Interface
In delete_app/admin.py
, customize the admin interface:
# delete_app/admin.py
from django.contrib import admin
from .models import Contact
@admin.register(Contact)
class ContactAdmin(admin.ModelAdmin):
list_display = ('first_name', 'last_name', 'email', 'phone_number', 'message', 'is_deleted')
list_filter = ('is_deleted',)
search_fields = ('first_name', 'last_name', 'email', 'phone_number', 'message')
actions = ['restore_selected']
def restore_selected(modeladmin, request, queryset):
queryset.update(is_deleted=False)
restore_selected.short_description = "Restore selected contacts"
Conclusion
You’ve now implemented soft delete functionality in your Django application. This approach helps you keep track of deleted records and allows for data recovery if needed. The provided steps cover model definition, view creation, template setup, and admin configuration, ensuring a comprehensive implementation. Happy coding!
Very interesting, helpfull and easy to understood, thanks a lot for sharing your knowledge. that is the way to improve the world and make a sociaty more equal. I replicated in a case of study on my university, it help me a lot.
by the way, can I ask you a question? Is there any way in django to make the delete bottom in Django Admin to works as soft Delete when clicking on it. I meant what I would like to the delete bottom do after clicking on it is to works as soft delete instead of remove the records as it works by default: