Building your first CRUD application with Django
5 min published: 1/14/2026
You have explored models, views, and templates. It’s time to put it all together to build something concrete! This tutorial will guide you, step by step, in creating your first complete web application with Django.
We are going to build a mini note management system, a simple CRUD application.
What does CRUD mean?
CRUD is an acronym that designates the four basic operations for data management:
- Create: Create new data.
- Read: Read (display) existing data.
- Update: Update existing data.
- Delete: Delete existing data.
Mastering CRUD means mastering the foundation of most dynamic web applications.
To follow this guide, you must have Python and Django installed and a basic understanding of models, views, and templates.
Step 1: project and application creation
Open your terminal and let’s create the basic structure.
-
Create the Django project:
django-admin startproject mynotesproject cd mynotesproject -
Create the “notes” application:
python manage.py startapp notes -
Register the application: Open
mynotesproject/settings.pyand add'notes'to yourINSTALLED_APPSlist:# mynotesproject/settings.py INSTALLED_APPS = [ # ... other apps 'notes.apps.NotesConfig', ]
Step 2: The data model
Let’s define the structure of our notes.
In notes/models.py, create the Note model:
# notes/models.py
from django.db import models
from django.urls import reverse
class Note(models.Model):
title = models.CharField(max_length=200)
content = models.TextField()
created_at = models.DateTimeField(auto_now_add=True)
def __str__(self):
return self.title
def get_absolute_url(self):
return reverse('note-detail', kwargs={'pk': self.pk})
The get_absolute_url method is a very useful Django convention. It returns the canonical URL for an object, which allows Django to know where to redirect the user after a creation or update operation.
Apply this model to the database:
python manage.py makemigrations
python manage.py migrate
Step 3: READ - displaying notes
Let’s start by displaying the existing notes.
The view (list and detail)
We will use generic Class-Based Views (CBV), as they are perfect for CRUD.
Modify notes/views.py:
# notes/views.py
from django.views.generic import ListView, DetailView
from .models import Note
class NoteListView(ListView):
model = Note
template_name = 'notes/note_list.html'
context_object_name = 'notes'
class NoteDetailView(DetailView):
model = Note
template_name = 'notes/note_detail.html'
context_object_name = 'note'
The templates (list and detail)
Create a templates/notes folder in your notes application.
notes/templates/notes/note_list.html
<h1>My Notes</h1>
<a href="{% url 'note-create' %}">Add a note</a>
<ul>
{% for note in notes %}
<li><a href="{{ note.get_absolute_url }}">{{ note.title }}</a></li>
{% empty %}
<li>No notes for now.</li>
{% endfor %}
</ul>
notes/templates/notes/note_detail.html
<h1>{{ note.title }}</h1>
<p>{{ note.content|linebreaks }}</p>
<small>Created on: {{ note.created_at }}</small>
<hr />
<a href="{% url 'note-update' note.pk %}">Edit</a>
<a href="{% url 'note-delete' note.pk %}">Delete</a>
<a href="{% url 'note-list' %}">Back to list</a>
The |linebreaks filter transforms line breaks in the text into <p> and <br> tags.
The URLs
Create a notes/urls.py file:
# notes/urls.py
from django.urls import path
from .views import NoteListView, NoteDetailView
urlpatterns = [
path('', NoteListView.as_view(), name='note-list'),
path('note/<int:pk>/', NoteDetailView.as_view(), name='note-detail'),
]
Include these URLs in the main project mynotesproject/urls.py:
# mynotesproject/urls.py
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('notes.urls')), # Include notes app URLs
]
Start the server (python manage.py runserver) and visit http://127.0.0.1:8000/. You should see your list of notes (empty for now).
Step 4: CREATE - creating a note with ModelForm
To create notes, we need forms. Django excels at this with ModelForm.
The form
Create a notes/forms.py file:
# notes/forms.py
from django import forms
from .models import Note
class NoteForm(forms.ModelForm):
class Meta:
model = Note
fields = ['title', 'content']
That’s it! Django will automatically generate a form based on the title and content fields of our Note model.
The view (CreateView)
Add CreateView to notes/views.py:
# notes/views.py
from django.views.generic import ListView, DetailView, CreateView
from .models import Note
from .forms import NoteForm
# ... NoteListView and NoteDetailView ...
class NoteCreateView(CreateView):
model = Note
form_class = NoteForm
template_name = 'notes/note_form.html'
# success_url will be handled by get_absolute_url on the model
The form template
Create notes/templates/notes/note_form.html. This template will serve for both creation and modification.
<h1>{% if object %}Edit Note{% else %}New Note{% endif %}</h1>
<form method="post">
{% csrf_token %} {{ form.as_p }}
<button type="submit">Save</button>
</form>
{% csrf_token %}is a mandatory security measure forPOSTforms.{{ form.as_p }}displays each form field in a<p>paragraph.
The URL
Add the path in notes/urls.py:
# notes/urls.py
# ...
from .views import NoteListView, NoteDetailView, NoteCreateView
urlpatterns = [
# ...
path('note/new/', NoteCreateView.as_view(), name='note-create'),
]
You can now create notes!
Step 5: UPDATE - updating a note
The update view is very similar to the create view.
The view (UpdateView)
Add UpdateView to notes/views.py:
# notes/views.py
from django.views.generic import ListView, DetailView, CreateView, UpdateView
# ...
class NoteUpdateView(UpdateView):
model = Note
form_class = NoteForm
template_name = 'notes/note_form.html'
We reuse the same note_form.html template.
The URL
Add the path in notes/urls.py:
# notes/urls.py
# ...
from .views import NoteListView, NoteDetailView, NoteCreateView, NoteUpdateView
urlpatterns = [
# ...
path('note/<int:pk>/edit/', NoteUpdateView.as_view(), name='note-update'),
]
Now, the “Edit” link on the detail page works.
Step 6: DELETE - deleting a note
The last operation of CRUD. Django asks for confirmation before deleting.
The view (DeleteView)
Add DeleteView to notes/views.py:
# notes/views.py
from django.urls import reverse_lazy
from django.views.generic import ListView, DetailView, CreateView, UpdateView, DeleteView
# ...
class NoteDeleteView(DeleteView):
model = Note
template_name = 'notes/note_confirm_delete.html'
success_url = reverse_lazy('note-list')
reverse_lazy is used here because the URL is evaluated only at runtime, not when the file is loaded.
The confirmation template
Create notes/templates/notes/note_confirm_delete.html:
<h1>Delete Note</h1>
<p>Are you sure you want to delete the note: "{{ note.title }}"?</p>
<form method="post">
{% csrf_token %}
<button type="submit">Confirm deletion</button>
<a href="{{ note.get_absolute_url }}">Cancel</a>
</form>
The URL
Add the path in notes/urls.py:
# notes/urls.py
# ...
from .views import (
NoteListView,
NoteDetailView,
NoteCreateView,
NoteUpdateView,
NoteDeleteView
)
urlpatterns = [
# ...
path('note/<int:pk>/delete/', NoteDeleteView.as_view(), name='note-delete'),
]
Your CRUD application is complete!
Conclusion
Congratulations! You have just built a fully functional web application with Django, mastering the four CRUD operations. By using generic views (ListView, DetailView, CreateView, UpdateView, DeleteView) and ModelForm, you can build the foundation of any data management application with remarkable speed and efficiency.
Next steps could be:
- Styling the templates with CSS.
- Adding authentication so each user has their own notes.
- Deploying your application to production.