CRUD stands for Create, Read, Update, and Delete. These four operations are the foundation of many database-driven web applications.
In this lesson, we will understand how to build a simple Django CRUD project using a Student model. The project will allow an administrator or user to add students, view students, edit student information, and delete students.
CRUD represents four basic database operations:
Create → Add Student
Read → View Students
Update → Edit Student
Delete → Delete Student
Suppose we are building a student management system.
Each student may have:
Our CRUD system will allow us to manage these records from a web interface.
First, create an application for the CRUD functionality.
python manage.py startapp students
Then add the application to INSTALLED_APPS in
settings.py.
INSTALLED_APPS = [
...
"students",
]
Create the Student model in students/models.py.
from django.db import models
class Student(models.Model):
name = models.CharField(
max_length=100
)
email = models.EmailField()
course = models.CharField(
max_length=100
)
age = models.IntegerField()
def __str__(self):
return self.name
After creating or changing a model, create and apply migrations.
python manage.py makemigrations
python manage.py migrate
The migration system creates the required database table.
A ModelForm can automatically create form fields from the Student model.
Create students/forms.py:
from django import forms
from .models import Student
class StudentForm(forms.ModelForm):
class Meta:
model = Student
fields = [
"name",
"email",
"course",
"age"
]
The create view displays the form and saves a new student after a valid POST request.
from django.shortcuts import render, redirect
from .forms import StudentForm
def student_create(request):
if request.method == "POST":
form = StudentForm(
request.POST
)
if form.is_valid():
form.save()
return redirect(
"student_list"
)
else:
form = StudentForm()
return render(
request,
"students/student_form.html",
{
"form": form
}
)
Create a template for the student form.
<h2>Add Student</h2>
<form method="post">
{% csrf_token %}
{{ form.as_p }}
<button type="submit">
Save Student
</button>
</form>
The csrf_token protects the POST form against CSRF attacks.
The read operation retrieves records from the database.
from .models import Student
def student_list(request):
students = Student.objects.all()
return render(
request,
"students/student_list.html",
{
"students": students
}
)
<h2>Student List</h2>
<table>
<tr>
<th>Name</th>
<th>Email</th>
<th>Course</th>
<th>Age</th>
</tr>
{% for student in students %}
<tr>
<td>
{{ student.name }}
</td>
<td>
{{ student.email }}
</td>
<td>
{{ student.course }}
</td>
<td>
{{ student.age }}
</td>
</tr>
{% endfor %}
</table>
The update operation retrieves an existing student and passes it to the
ModelForm using the instance argument.
from django.shortcuts import get_object_or_404
def student_update(request, id):
student = get_object_or_404(
Student,
id=id
)
if request.method == "POST":
form = StudentForm(
request.POST,
instance=student
)
if form.is_valid():
form.save()
return redirect(
"student_list"
)
else:
form = StudentForm(
instance=student
)
return render(
request,
"students/student_form.html",
{
"form": form
}
)
The delete operation removes a record from the database.
def student_delete(request, id):
student = get_object_or_404(
Student,
id=id
)
if request.method == "POST":
student.delete()
return redirect(
"student_list"
)
return render(
request,
"students/student_confirm_delete.html",
{
"student": student
}
)
get_object_or_404() retrieves an object or returns a 404
response when the requested object does not exist.
student = get_object_or_404(
Student,
id=id
)
This is safer and cleaner than assuming that the requested record always exists.
Create students/urls.py.
from django.urls import path
from . import views
urlpatterns = [
path(
"",
views.student_list,
name="student_list"
),
path(
"add/",
views.student_create,
name="student_create"
),
path(
"edit/<int:id>/",
views.student_update,
name="student_update"
),
path(
"delete/<int:id>/",
views.student_delete,
name="student_delete"
),
]
Include the student app URLs in the main project's
urls.py.
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path(
"admin/",
admin.site.urls
),
path(
"students/",
include("students.urls")
),
]
Now the student list can be accessed through:
/students/
The student list page can contain a link to the create page.
<a href="{% url 'student_create' %}">
Add Student
</a>
Using the URL name is better than hard-coding the complete URL.
Each student can have an Edit link.
<a href="{% url 'student_update' student.id %}">
Edit
</a>
The student's ID is passed to the URL parameter.
A POST form can be used for deletion.
<form
method="post"
action="{% url 'student_delete' student.id %}"
>
{% csrf_token %}
<button type="submit">
Delete
</button>
</form>
Using POST for destructive operations helps avoid deleting records through simple GET requests.
<h2>
Delete Student
</h2>
<p>
Are you sure you want to delete
{{ student.name }}?
</p>
<form method="post">
{% csrf_token %}
<button type="submit">
Yes, Delete
</button>
</form>
<a href="{% url 'student_list' %}">
Cancel
</a>
The user gets a chance to cancel before the record is deleted.
A simple CRUD project can use the following template structure:
students/
└── templates/
└── students/
├── student_list.html
├── student_form.html
└── student_confirm_delete.html
Keeping related templates inside the app makes the project easier to organize.
myproject/
│
├── manage.py
│
├── myproject/
│ ├── settings.py
│ ├── urls.py
│ ├── asgi.py
│ └── wsgi.py
│
└── students/
├── migrations/
├── templates/
│ └── students/
│ ├── student_list.html
│ ├── student_form.html
│ └── student_confirm_delete.html
├── __init__.py
├── admin.py
├── apps.py
├── forms.py
├── models.py
├── urls.py
├── views.py
└── tests.py
Bootstrap can be used to make the CRUD pages easier to use.
<div class="container mt-4">
<h2>Student List</h2>
<a
href="{% url 'student_create' %}"
class="btn btn-primary"
>
Add Student
</a>
</div>
Bootstrap classes can also be applied to forms, tables, buttons, alerts, and navigation.
A CRUD application can be combined with the Django search system learned in the previous lesson.
query = request.GET.get(
"q",
""
).strip()
students = Student.objects.all()
if query:
students = students.filter(
name__icontains=query
)
This allows users to find a record before editing or deleting it.
When the database contains many records, pagination can be added to the student list.
from django.core.paginator import Paginator
paginator = Paginator(
students,
10
)
page_number = request.GET.get(
"page"
)
page_obj = paginator.get_page(
page_number
)
The template can then display page_obj instead of the complete
QuerySet.
Django's messages framework can provide feedback after CRUD operations.
from django.contrib import messages
form.save()
messages.success(
request,
"Student added successfully."
)
return redirect(
"student_list"
)
Similar messages can be created after updating or deleting records.
Add Student
↓
Display Form
↓
Submit POST Request
↓
Validate Form
↓
Save Record
↓
Redirect to List
View Students
↓
Query Database
↓
Display Records
Edit Student
↓
Get Existing Record
↓
Load ModelForm
↓
Validate Form
↓
Save Changes
↓
Redirect to List
Delete Student
↓
Get Existing Record
↓
Confirm Delete
↓
Delete Record
↓
Redirect to List
INSTALLED_APPS.csrf_token in POST forms.instance while updating.get_object_or_404.from django.shortcuts import (
render,
redirect,
get_object_or_404
)
from django.contrib import messages
from .models import Student
from .forms import StudentForm
def student_list(request):
students = Student.objects.all()
return render(
request,
"students/student_list.html",
{
"students": students
}
)
def student_create(request):
if request.method == "POST":
form = StudentForm(
request.POST
)
if form.is_valid():
form.save()
messages.success(
request,
"Student added successfully."
)
return redirect(
"student_list"
)
else:
form = StudentForm()
return render(
request,
"students/student_form.html",
{
"form": form
}
)
def student_update(request, id):
student = get_object_or_404(
Student,
id=id
)
if request.method == "POST":
form = StudentForm(
request.POST,
instance=student
)
if form.is_valid():
form.save()
messages.success(
request,
"Student updated successfully."
)
return redirect(
"student_list"
)
else:
form = StudentForm(
instance=student
)
return render(
request,
"students/student_form.html",
{
"form": form
}
)
def student_delete(request, id):
student = get_object_or_404(
Student,
id=id
)
if request.method == "POST":
student.delete()
messages.success(
request,
"Student deleted successfully."
)
return redirect(
"student_list"
)
return render(
request,
"students/student_confirm_delete.html",
{
"student": student
}
)
Student Model
↓
Database Migration
↓
ModelForm
↓
Create View
↓
Read/List View
↓
Update View
↓
Delete View
↓
URL Patterns
↓
HTML Templates
↓
CRUD Application
A complete CRUD application combines models, forms, views, URLs, templates, and database operations into one working system.
The Django CRUD project demonstrates the complete lifecycle of database records.
ModelForm to simplify form handling.get_object_or_404() for safe record retrieval.form.save() creates or updates a record.student.delete() removes a record.get_object_or_404() safely retrieves an object.{% csrf_token %}.Question: What does the "U" in CRUD stand for?