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:
mkdir djangoCrud
cd djangoCrud
Create the virtual environment with Python 3:
python3 -m venv venv
Then activate the virtual environment:
. venv/bin/activate
Upgrade the virtual environment:
pip install --upgrade pip setuptools
Install Django:
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:
django-admin startproject crudCbv
Enter the crudCBV folder and run the project to see if everything is working:
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:
python manage.py startapp books
Go to crudCBV/settings.py and register the books app:
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:
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:
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:
# Static files (CSS, JavaScript, Images)
# https://docs.djangoproject.com/en/3.0/howto/static-files/
STATIC_URL = '/static/'
STATICFILES_DIRS = [
os.path.join(BASE_DIR, 'static'),
]
Enter the templates folder and create the file base.html:
{% 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:
body {
background-color: white;
padding-top: 4.5rem;
}
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:
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:
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:
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:
{% 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):
{% 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):
{% 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):
{% 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):
{% 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:
"""crudCBV URL Configuration
The `urlpatterns` list routes URLs to views. For more information please see:
https://docs.djangoproject.com/en/3.0/topics/http/urls/
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:
python manage.py makemigrations
python manage.py migrate
Now run the project:
python manage.py runserver
If everything was fine you should the following:
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!