Django CRUD Operations: Step-by-Step Guide
This tutorial assumes you have already created your project (todo_site) and your app (tasks), and registered the app in settings.py. We will now build the CRUD functionality one operation at a time.
Step 0: The Foundation (Model)
Before we can do any CRUD operations, we need a database table to hold our data.
-
Define the Model in
tasks/models.py:from django.db import models class Task(models.Model): title = models.CharField(max_length=200) completed = models.BooleanField(default=False) created_at = models.DateTimeField(auto_now_add=True) def __str__(self): return self.title -
Create the Database Table: Run this in your terminal:
python manage.py makemigrations python manage.py migrate
Operation 1: READ (Displaying the List)
The "R" in CRUD. Let's just get a list of tasks showing up on the screen.
-
Create the View (
tasks/views.py): We fetch all tasks from the database and send them to the template.from django.shortcuts import render from .models import Task def task_list(request): tasks = Task.objects.all() # Fetch all data return render(request, 'tasks/list.html', {'tasks': tasks}) -
Create the URL (todo_list
/urls.py): Map the empty path''to our view.from django.urls import path, include from . import views urlpatterns = [ path('', include('tasks.urls')), ] -
Create the URL (
tasks/urls.py): Map the empty path''to our view.from django.urls import path from . import views urlpatterns = [ path('', views.task_list, name='list'), ] -
Create the Template (
templates/tasks/list.html): A simple HTML loop to display data.<h1>My To-Do List</h1> <ul> {% for task in tasks %} <li>{{ task.title }}</li> {% empty %} <li>No tasks yet!</li> {% endfor %} </ul>
Checkpoint: Run python manage.py runserver and go to http://127.0.0.1:8000/. You should see "My To-Do List" and "No tasks yet!".
Operation 2: CREATE (Adding Data)
The "C" in CRUD. Now we need a form to add new tasks.
-
Define the Form (
tasks/forms.py): Create this file to let Django handle the HTML form generation.from django import forms from .models import Task class TaskForm(forms.ModelForm): class Meta: model = Task fields = ['title', 'completed'] -
Update the View (
tasks/views.py): Modifytask_listto handle data submission (POST requests).from django.shortcuts import render, redirect # Import redirect from .models import Task from .forms import TaskForm # Import the Form def task_list(request): tasks = Task.objects.all() form = TaskForm() # Empty form for GET request if request.method == 'POST': form = TaskForm(request.POST) # Fill form with data if form.is_valid(): form.save() # Save to database return redirect('list') # Refresh page return render(request, 'tasks/list.html', {'tasks': tasks, 'form': form}) -
Update the Template (
templates/tasks/list.html): Add the form code above the list.<h1>My To-Do List</h1> <!-- New Form Section --> <form method="POST"> {% csrf_token %} {{ form.as_p }} <button type="submit">Add Task</button> </form> <hr> <ul> {% for task in tasks %} <li>{{ task.title }}</li> {% endfor %} </ul>
Checkpoint: Refresh your browser. You can now type a task title, click "Add Task", and see it appear in the list immediately.
Operation 3: UPDATE (Editing Data)
The "U" in CRUD. We need a dedicated page to edit a specific task.
-
Create the View (
tasks/views.py): We need a new view that finds a specific task by its ID (pk) and fills the form with existing data.# Add 'get_object_or_404' to imports from django.shortcuts import render, redirect, get_object_or_404 # ... existing task_list view ... def task_update(request, pk): task = get_object_or_404(Task, id=pk) # Find task by ID form = TaskForm(instance=task) # Fill form with existing data if request.method == 'POST': form = TaskForm(request.POST, instance=task) if form.is_valid(): form.save() return redirect('list') return render(request, 'tasks/update.html', {'form': form}) -
Create the URL (
tasks/urls.py): We need a URL that accepts an ID number (likeupdate/1/).urlpatterns = [ path('', views.task_list, name='list'), # New URL pattern path('update/<int:pk>/', views.task_update, name='update'), ] -
Create the Template (
templates/tasks/update.html): A separate page just for editing.<h2>Update Task</h2> <form method="POST"> {% csrf_token %} {{ form.as_p }} <button type="submit">Update</button> </form> -
Link it in the List (
templates/tasks/list.html): Add an "Edit" link next to each item.<li> {{ task.title }} <a href="{% url 'update' task.id %}">Edit</a> <!-- New Link --> </li>
Checkpoint: Refresh the home page. Click "Edit" next to a task. You will go to a new page, change the title or checkbox, click Update, and return to the home page with the changes saved.
Operation 4: DELETE (Removing Data)
The "D" in CRUD. Ideally, we ask for confirmation before deleting.
-
Create the View (
tasks/views.py): Find the task, wait for a POST (confirmation) request, then delete.# ... existing views ... def task_delete(request, pk): task = get_object_or_404(Task, id=pk) if request.method == 'POST': task.delete() # Delete from database return redirect('list') return render(request, 'tasks/delete_confirm.html', {'task': task}) -
Create the URL (
tasks/urls.py):urlpatterns = [ path('', views.task_list, name='list'), path('update/<int:pk>/', views.task_update, name='update'), # New URL pattern path('delete/<int:pk>/', views.task_delete, name='delete'), ] -
Create the Template (
templates/tasks/delete_confirm.html): A warning page.<h2>Are you sure?</h2> <p>Do you want to delete "{{ task.title }}"?</p> <form method="POST"> {% csrf_token %} <button type="submit">Yes, Delete</button> <a href="{% url 'list' %}">Cancel</a> </form> -
Link it in the List (
templates/tasks/list.html): Add a "Delete" link next to the Edit link.<li> {{ task.title }} <a href="{% url 'update' task.id %}">Edit</a> <a href="{% url 'delete' task.id %}">Delete</a> <!-- New Link --> </li>