
Here I want to present a simple implementation of a CRUD with class based views of Django.
Are you in a hurry?
If so, go straight to the working version of this project: https://github.com/albertosdneto/django_simple_crud_cbv
If you want to read just a little more, go on reading the rest of this post.
Virtual Environment
First thing we need to do is to prepare a virtual environment for our project. Open the console (on Linux) and proceed as follows:
Create a directory and enter:
1 2 | mkdir djangoCrud cd djangoCrud |
Create the virtual environment with Python 3:
1 | python3 -m venv venv |
Then activate the virtual environment:
1 | . venv/bin/activate |
Upgrade the virtual environment:
1 | pip install --upgrade pip setuptools |
Install Django:
1 | pip install django |
Project Creation and Setup
So far we have prepared our environment to develop our project with a certain control of the dependencies. Now we want to create the Django project.
Create the project:
1 | django-admin startproject crudCbv |
Enter the crudCBV folder and run the project to see if everything is working:
1 2 | cd crudCBV python manage.py runserver |
Go to http://127.0.0.1:8000/ and you should see the page below:

Stop the sever (use Ctrl + c on terminal) and create the app books:
1 | python manage.py startapp books |
Go to crudCBV/settings.py and register the books app:
33 34 35 36 37 38 39 40 41 | INSTALLED_APPS = [ 'django.contrib.admin' , 'django.contrib.auth' , 'django.contrib.contenttypes' , 'django.contrib.sessions' , 'django.contrib.messages' , 'django.contrib.staticfiles' , 'books' , ] |
Create the folders templates and static at the same level as books app. At this moment our directory tree should look like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | crudCBV/ ├── books │ ├── admin.py │ ├── apps.py │ ├── __init__.py │ ├── migrations │ │ └── __init__.py │ ├── models.py │ ├── tests.py │ └── views.py ├── crudCBV │ ├── asgi.py │ ├── __init__.py │ ├── __pycache__ │ │ ├── __init__.cpython-36.pyc │ │ ├── settings.cpython-36.pyc │ │ ├── urls.cpython-36.pyc │ │ └── wsgi.cpython-36.pyc │ ├── settings.py │ ├── urls.py │ └── wsgi.py ├── db.sqlite3 ├── manage.py ├── static └── templates |
Go back to the file settings.py and configure it for the folder templates:
55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 | TEMPLATES = [ { 'BACKEND' : 'django.template.backends.django.DjangoTemplates' , 'DIRS' : [ 'templates' ], 'APP_DIRS' : True , 'OPTIONS' : { 'context_processors' : [ 'django.template.context_processors.debug' , 'django.template.context_processors.request' , 'django.contrib.auth.context_processors.auth' , 'django.contrib.messages.context_processors.messages' , ], }, }, ] |
Still at settings.py configure it for static files:
118 119 120 121 122 123 124 | # Static files (CSS, JavaScript, Images) STATIC_URL = '/static/' STATICFILES_DIRS = [ os.path.join(BASE_DIR, 'static' ), ] |
Enter the templates folder and create the file base.html:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 | {% load static %} <! DOCTYPE html> < html lang = "en" > < head > < meta charset = "UTF-8" > < meta name = "viewport" content = "width=device-width, initial-scale=1.0" > < meta http-equiv = "X-UA-Compatible" content = "ie=edge" > <!-- Principal CSS do Bootstrap --> < link rel = "stylesheet" href = "https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" integrity = "sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin = "anonymous" > < link rel = "stylesheet" href = "{% static 'css/master.css'%}" > < title >Django simple crud with cbv</ title > </ head > < body > < nav class = "navbar navbar-expand-md navbar-dark fixed-top bg-info" > < button class = "navbar-toggler" type = "button" data-toggle = "collapse" data-target = "#navbarCollapse" aria-controls = "navbarCollapse" aria-expanded = "false" aria-label = "Toggle navigation" > < span class = "navbar-toggler-icon" ></ span > </ button > < div class = "collapse navbar-collapse" id = "navbarCollapse" > < ul class = "navbar-nav ml-auto mr-auto" > < li class = "nav-item active" > < a class = "nav-link" href = "{% url 'home' %}" >Home</ a > </ li > < li class = "nav-item active" > < a class = "nav-link" href = "{% url 'create' %}" >New</ a > </ li > </ ul > </ div > </ nav > < main role = "main" class = "container" > < div class = "container mt-5 mb-5 page-section" > < div class = "col" > {% block content %} {% endblock content %} </ div > </ div > </ main > <!-- Principal JavaScript do Bootstrap ================================================== --> < script src = "https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity = "sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin = "anonymous" ></ script > < script > window.jQuery || document.write('< script src = "../../assets/js/vendor/jquery-slim.min.js" ><\/script>') </ script > < script src = "https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.14.3/umd/popper.min.js" integrity = "sha384-ZMP7rVo3mIykV+2+9J3UJ46jBk0WLaUAdn689aCwoqbBJiSnjAK/l8WvCWPIPm49" crossorigin = "anonymous" ></ script > < script src = "https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/js/bootstrap.min.js" integrity = "sha384-ChfqqxuZUCnJSK3+MXmPNIyE6ZbWh2IMqE241rYiqJxyMiZ6OW/JmZQ5stwEULTy" crossorigin = "anonymous" ></ script > </ body > </ html > |
Go to the static folder and create the folder css and inside it create the file master.css:
1 2 3 4 | body { background-color : white ; padding-top : 4.5 rem; } |
Model for our data
In order to persist data on sqlite we need to create a model for it. Inside the books app open the file models.py and paste the code below:
1 2 3 4 5 6 7 | from django.db import models # Create your models here. class Book(models.Model): title = models.CharField(max_length = 250 ) author = models.CharField(max_length = 250 ) pages = models.IntegerField() |
Inside the database our table will have the columns title, author and pages.
Views for our books app
Let’s create the views for our app. Inside the folder books open the file views.py and make it the way it is below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 | from .models import Book from django.shortcuts import render from django.views.generic import DetailView from django.views.generic. list import ListView from django.views.generic.edit import CreateView, UpdateView, DeleteView class BookListView(ListView): model = Book class BookCreateView(CreateView): model = Book fields = [ 'title' , 'author' , 'pages' ] success_url = '/' class BookDetailView(DetailView): model = Book class BookUpdateView(UpdateView): model = Book fields = [ 'title' , 'author' , 'pages' ] success_url = '/' class BookDeleteView(DeleteView): model = Book success_url = '/' |
When we use Class Based Views, Django expects us to provide templates with certain name patterns. So, inside the folder books, create a folder templates and a sub-folder books. inside this new books folder create the files book_confirm_delete.html, book_detail.html, book_form.html, book_list.html and book_update_form.html.
Our directory tree for books app should be like this:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | books/ ├── admin.py ├── apps.py ├── __init__.py ├── migrations │ └── __init__.py ├── models.py ├── templates │ └── books │ ├── book_confirm_delete.html │ ├── book_detail.html │ ├── book_form.html │ ├── book_list.html │ └── book_update_form.html ├── tests.py └── views.py |
For file book_confirm_delete.html (necessary for BookDeleteView) we should have:
1 2 3 4 5 6 7 8 9 | {% extends 'base.html' %} {% block content %} < form method = 'post' > {% csrf_token %} < p >Are you sure you want to delete?</ p > < input type = "submit" Value = "Delete" > </ form > {% endblock content%} |
For file book_detail.html (BookDetailView):
1 2 3 4 5 6 7 8 9 | {% extends 'base.html' %} {% block content %} <p> <strong>Title: </strong>{{ object.title }}<br/> <strong>Author: </strong>{{ object.author }}<br/> <strong>Pages: </strong>{{ object.pages }}<br/> </p> {% endblock content %} |
For file book_form.html (BookCreateView):
1 2 3 4 5 6 7 8 9 10 11 | {% extends 'base.html' %} {% block content %} <form method='post'> {% csrf_token %} {{ form.as_p }} <input type='submit' value='save'> </form> {% endblock content %} |
For book_list.html (BookListView):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | {% extends 'base.html'%} {% block content %} {% if object_list %} <ul> {% for book in object_list %} <li> <a href="{% url 'detail' book.id %}">{{book.title}}</a> | <a href="{% url 'update' book.id %}">Update</a> | <a href="{% url 'delete' book.id %}">Delete</a> </li> {% endfor %} </ul> {% else %} <p>No book on database.</p> {% endif %} {% endblock content%} |
For book_update_form.html (BookUpdateView):
1 2 3 4 5 6 7 8 9 10 11 | {% extends 'base.html' %} {% block content %} <form method='post'> {% csrf_token %} {{ form.as_p }} <input type='submit' value='save'> </form> {% endblock content %} |
URLs for our app
For the sake of simplicity at this tutorial, we are going to keep our urls at the file urls.py contained at crudCBV folder. Open it and make it as below:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | """crudCBV URL Configuration The `urlpatterns` list routes URLs to views. For more information please see: Examples: Function views 1. Add an import: from my_app import views 2. Add a URL to urlpatterns: path('', views.home, name='home') Class-based views 1. Add an import: from other_app.views import Home 2. Add a URL to urlpatterns: path('', Home.as_view(), name='home') Including another URLconf 1. Import the include() function: from django.urls import include, path 2. Add a URL to urlpatterns: path('blog/', include('blog.urls')) """ from django.contrib import admin from django.urls import path, include from books.views import BookListView, BookCreateView, BookDetailView, BookUpdateView, BookDeleteView urlpatterns = [ path( 'admin/' , admin.site.urls), path(' ', BookListView.as_view(), name=' home'), path( 'create/' , BookCreateView.as_view(), name = 'create' ), path( 'detail/<int:pk>' , BookDetailView.as_view(), name = 'detail' ), path( 'update/<int:pk>' , BookUpdateView.as_view(), name = 'update' ), path( 'delete/<int:pk>' , BookDeleteView.as_view(), name = 'delete' ), ] |
Running the app
Now that all our code is in place, let’s run it.
First, make migrations in order to prepare the database:
1 2 | python manage.py makemigrations python manage.py migrate |
Now run the project:
1 | python manage.py runserver |
If everything was fine you should the following:
1 2 3 4 5 6 7 8 | Watching for file changes with StatReloader Performing system checks... System check identified no issues (0 silenced). January 26, 2020 - 16:30:39 Django version 3.0.2, using settings 'crudCBV.settings' Starting development server at http: //127 .0.0.1:8000/ Quit the server with CONTROL-C. |
At http://127.0.0.1:8000/ you should see:

Test it. Try to add some entries, edit and delete.
Good Luck!