CRUD

Django CRUD Roadmap

This page shows a complete roadmap for building a simple Django CRUD app (Create, Read, Update, Delete) using a Person model.

CRUD stands for Create, Read, Update, Delete. In this mini project, we will:

  • Create people (name, surname, email) using a form on the homepage.
  • Read all people in a Bootstrap table on the /data/ page.
  • Update existing records using an edit form.
  • Delete records from the database with a delete button.
Goal: Simple and clean CRUD example with Django models, views, templates and Bootstrap.

Terminal commands to set up the project and app:

Terminal
0) Create empty file
1) PS C:\Users\Alvin\Desktop\CRUD-roadmap> mkdir core
2) PS C:\Users\Alvin\Desktop\CRUD-roadmap> cd .\core\
3) PS C:\Users\Alvin\Desktop\CRUD-roadmap\core> python -m venv .venv
4) PS C:\Users\Alvin\Desktop\CRUD-roadmap\core> .venv\Scripts\activate
5) PS C:\Users\Alvin\Desktop\CRUD-roadmap\core> pip install Django
6) PS C:\Users\Alvin\Desktop\CRUD-roadmap\core> django-admin startproject core .
7) PS C:\Users\Alvin\Desktop\CRUD-roadmap\core> python manage.py startapp home
8) PS C:\Users\Alvin\Desktop\CRUD-roadmap\core> python manage.py runserver
Structure is ready ✅ – You now have a Django project core and app home.

Open core/core/settings.py and add home into INSTALLED_APPS:

core/core/settings.py
INSTALLED_APPS = [
    ...
    'home',
]

1) First migration
1) PS C:\Users\Alvin\Desktop\CRUD-roadmap\core> python manage.py migrate
2) Add Person model

Open core/home/models.py and add:

from django.db import models

class Person(models.Model):
    name = models.CharField(max_length=50)
    surname = models.CharField(max_length=50)
    email = models.EmailField()

    def __str__(self):
        return f"Name: {self.name}"
3) Make migrations & migrate
2) PS C:\Users\Alvin\Desktop\CRUD-roadmap\core> python manage.py makemigrations
3) PS C:\Users\Alvin\Desktop\CRUD-roadmap\core> python manage.py migrate

Register the Person model in core/home/admin.py (not shown here), then create a superuser:

1) PS C:\Users\Alvin\Desktop\CRUD-roadmap\core> python manage.py createsuperuser

Username (leave blank to use 'alvin'): alvin
Email address: elvinbabanli0@gmail.com
Password: 12345678 (Password going to be invisible)
Password (again): 12345678 (Password going to be invisible)
Bypass password validation and create user anyway? [y/N]: y
Superuser created successfully.

1) core/core/urls.py

Add imports and include home.urls:

from django.contrib import admin
from django.urls import path, include
from home.views import *


urlpatterns = [
    path('admin/', admin.site.urls),
    path('', include('home.urls')),
]
2) core/home/urls.py

Create home/urls.py and add the CRUD routes:

from django.urls import path
from .views import index, get_data, delete_data, update_data

urlpatterns = [
    path('', index, name='main'),
    path('data/', get_data, name='get_data'),
    path('delete/int:person_id/', delete_data, name='delete_data'),
    path('update/int:person_id', update_data, name='update'),
]
Note: These routes map directly to the view functions that implement Create, Read, Update and Delete.

Create these HTML templates:

  • core/home/templates/base.html
  • core/home/templates/index.html
  • core/home/templates/data.html
  • core/home/templates/update.html

Also create a static folder:

  • core/home/static/
  • styles.css (optional extra styling)
You can use Bootstrap examples for modern tables and forms.

Add the views to core/home/views.py:

from django.shortcuts import render, redirect, get_object_or_404
from .models import Person

def index(request):
    edit_id = request.GET.get('edit')
    if request.method == 'GET' and edit_id:
        obj = get_object_or_404(Person, id=edit_id)
        return render(request, 'index.html', {'person': obj, 'is_edit': True})

    if request.method == 'POST':
        person_id = request.POST.get('person_id')
        name = request.POST.get('name')
        surname = request.POST.get('surname')
        email = request.POST.get('email')

        if person_id:
            obj = get_object_or_404(Person, id=person_id)
            obj.name = name
            obj.surname = surname
            obj.email = email
            obj.save()
            return redirect('/data/')
        else:
            Person.objects.create(name=name, surname=surname, email=email)
            return redirect('/')

    return render(request, 'index.html')

def get_data(request):
    people = Person.objects.all()
    return render(request, 'data.html', {'data': people})


def update_data(request, person_id):
    obj = get_object_or_404(Person, id=person_id)
    if request.method == 'POST':
        obj.name = request.POST.get('name')
        obj.surname = request.POST.get('surname')
        obj.email = request.POST.get('email')
        obj.save()
        return redirect('/data/')
    return render(request, 'update.html', {'data': obj})


def delete_data(request, person_id):
    obj = get_object_or_404(Person, id=person_id)
    obj.delete()
    return redirect('/data/')
  • Create – POST without person_id in index.
  • Readget_data shows all people in a table.
  • Update – either via edit query parameter in index or update_data view.
  • Deletedelete_data removes a record and redirects back to the table.

1) base.html
<!doctype html>
<html>
<head>
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link href="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/css/bootstrap.min.css" rel="stylesheet">
    <title>{% block title %}{% endblock %}</title>
</head>
<body class="p-3">
    <nav class="mb-3 d-flex gap-2">
        <a class="btn btn-primary" href="/">Home</a>
        <a class="btn btn-secondary" href="/data/">Data</a>
    </nav>
    {% block content %}{% endblock %}
    <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.3.3/dist/js/bootstrap.bundle.min.js"></script>
</body>
</html>
2) index.html – Create & inline edit form
{% extends 'base.html' %}
{% block title %}Home{% endblock %}
{% block content %}
<form method="post" class="card p-3 shadow-sm">
    {% csrf_token %}
    {% if is_edit %}
        <input type="hidden" name="person_id" value="{{ person.id }}">
    {% endif %}
    <div class="mb-3">
        <input class="form-control" name="name" placeholder="Name"
               value="{{ person.name|default:'' }}" required>
    </div>
    <div class="mb-3">
        <input class="form-control" name="surname" placeholder="Surname"
               value="{{ person.surname|default:'' }}" required>
    </div>
    <div class="mb-3">
        <input class="form-control" name="email" type="email" placeholder="Email"
               value="{{ person.email|default:'' }}" required>
    </div>

    <div class="d-flex gap-2 mt-2">
        <button class="btn btn-primary" type="submit">
            {% if is_edit %}Save Changes{% else %}Add User{% endif %}
        </button>

        {% if is_edit %}
            <a class="btn btn-secondary" href="/">Cancel</a>
        {% endif %}
    </div>
</form>
{% endblock %}
3) data.html – Read & action buttons
{% extends 'base.html' %}
{% block title %}Data{% endblock %}
{% block content %}
<table class="table table-striped align-middle">
    <thead>
        <tr>
            <th>#</th>
            <th>Name</th>
            <th>Surname</th>
            <th>Email</th>
            <th>Actions</th>
        </tr>
    </thead>
    <tbody>
        {% for p in data %}
        <tr>
            <th scope="row">{{ forloop.counter }}</th>
            <td>{{ p.name }}</td>
            <td>{{ p.surname }}</td>
            <td>{{ p.email }}</td>
            <td class="d-flex gap-2">
                <a href="/?edit={{ p.id }}" class="btn btn-sm btn-warning">Edit</a>
                <a href="/delete/{{ p.id }}/" class="btn btn-sm btn-danger">Delete</a>
            </td>
        </tr>
        {% endfor %}
    </tbody>
</table>
{% endblock %}
4) update.html – Separate update form
{% extends 'base.html' %}
{% block title %}Update{% endblock %}
{% block content %}
<form method="post" class="card p-3 shadow-sm">
    {% csrf_token %}
    <div class="mb-3">
        <input class="form-control" name="name" value="{{ data.name }}" required>
    </div>
    <div class="mb-3">
        <input class="form-control" name="surname" value="{{ data.surname }}" required>
    </div>
    <div class="mb-3">
        <input class="form-control" type="email" name="email" value="{{ data.email }}" required>
    </div>
    <button class="btn btn-primary" type="submit">Save</button>
</form>
{% endblock %}

Finally, run the development server:

1) PS C:\Users\Alvin\Desktop\CRUD-roadmap\core> python manage.py runserver
  • Open the link shown in the terminal (e.g. http://127.0.0.1:8000/).
  • Test:
    • / – create or edit a person.
    • /data/ – list, edit, delete records.
Your Django CRUD app is ready – you can now extend it with search, pagination, filters and more.
Elvin Babanlı
online • quick replies
×
Hi! I’m Elvin. Ask me anything about projects, stack, or your tasks.