John Bagiliko
9 min readApr 4, 2019

Build a Django CRUD Web Application (Part II)

In the previous tutorial, we started building a CRUD web application using the Python Framework called Django. This is a continuation from where we stopped so if you have not followed that tutorial, head to this before coming back to continue. In this post, we are going to work on the views, templates, URLS and beautify our frontend with Bootstrap to look similar to what we have above. Let’s get started!!

The Views

We will have five views (posts list view, detail view for a post, view for creating a post, view for editing a post, and one for deleting a post). Open the views.py file in Crud folder and paste the below codes:

from django.shortcuts import render, redirect, get_object_or_404
from .forms import PostForm
from .models import Post
from django.views.generic import ListView, DetailView
#home view for posts. Posts are displayed in a list
class IndexView(ListView):
template_name='Crud/index.html'
context_object_name = 'post_list'
def get_queryset(self):
return Post.objects.all()
#Detail view (view post detail)
class PostDetailView(DetailView):
model=Post
template_name = 'Crud/post-detail.html'
#New post view (Create new post)
def postview(request):
if request.method == 'POST':
form = PostForm(request.POST)
if form.is_valid():
form.save()
return redirect('index')
form = PostForm()
return render(request,'Crud/post.html',{'form': form})
#Edit a post
def edit(request, pk, template_name='Crud/edit.html'):
post= get_object_or_404(Post, pk=pk)
form = PostForm(request.POST or None, instance=post)
if form.is_valid():
form.save()
return redirect('index')
return render(request, template_name, {'form':form})
#Delete post
def delete(request, pk, template_name='Crud/confirm_delete.html'):
post= get_object_or_404(Post, pk=pk)
if request.method=='POST':
post.delete()
return redirect('index')
return render(request, template_name, {'object':post})

Whew!! That’s a lot of codes! Don’t worry if this is your first time using Django, you will get there in no time. In the above codes, we imported PostForm but we have not defined it yet. We also made reference to some html files which do not yet exist. We will do that in a jiffy.

Inside the Crud folder, create a file and name it forms.py (file should have this path ~/django_projects/CRUD/Crud/forms.py). Open it with a text editor, add the following codes and save:

from django import forms
from .models import Post
class PostForm(forms.ModelForm):
class Meta:
model = Post
fields = "__all__"

All we have done is import the Post model we defined in models.py in the previous post. We defined a class called PostForm, leveraging Django’s ModelForms in django.forms and specifying the model we want to use. We also specified that we will be using all fields in the Post model. This will make it possible for us to display those fields in our frontend (templates). Note that that this class (PostForm) has already been called and used in views.py. We are using this same form to create and edit posts.

URLS

URLS are very important in Django. They show which view function or class, defined in the views.py, is connected to which page you see on the browser or frontend.

  1. Create a file in the Crud folder and it urls.py. We have to tie this urls.py to the main urls.py in CRUD. Open the urls.py file in CRUD (~/django_projects/CRUD/CRUD/urls.py) and edit it to look like the one below.
from django.contrib import admin
from django.urls import path, include #add include
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('Crud.urls')), #add this line
]

2. Now that we have tied our app urls to the project urls, let us now define the url paths in our app. open the urls.py in Crud that is ~/django_projects/CRUD/Crud/urls.py and add the codes below:

from django.urls import path
from . import views
urlpatterns = [
path('', views.IndexView.as_view(), name='index'),
path('<int:pk>/', views.PostDetailView.as_view(), name='detail'),
path('edit/<int:pk>/', views.edit, name='edit'),
path('post/', views.postview, name='post'),
path('delete/<int:pk>/', views.delete, name='delete'),
]

Save and close this file.

Templates

It is time for us to create the beautiful frontend of our application. We will use Bootstrap in doing this.

  1. To make our templates recognizable by Django, head to the settings.py file in CRUD and open it with a text editor. Scroll down to the TEMPLATES section as in the image below:

2. Put the code below in the ‘DIRS’ line:

[os.path.join(BASE_DIR, 'templates')],

You should have something like the below:

3. Create a folder inside Crud and name it templates, that is ~/django_projects/CRUD/Crud/templates.

4. Create another folder inside templates and name it Crud, that is ~/django_projects/CRUD/Crud/templates/Crud.

5. Inside this newly created Crud folder, create the following files:

base.html, confirm_delete.html, edit.html, index.html, post.html and post-detail.html.

6. Open the base.html file (~/django_projects/CRUD/Crud/templates/Crud/base.html) with a text editor and paste the following codes:

<!DOCTYPE html>
<html>
<head>
<title>CRUD app</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.0/css/bootstrap.min.css">
</head>
<body>
{% block content %}{% endblock %}<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></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://ajax.googleapis.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.4.0/js/bootstrap.min.js"></script>
</body>
</html>

The base.html file will contain most of the base thingswhich will be extended by other pages.

7. Open the index.html file (~/django_projects/CRUD/Crud/templates/Crud/index.html) with a text editor and paste the following codes:

{% extends 'Crud/base.html' %}
{% block content %}
<div class="container-fluid">
<div class="row">
<div class="col-md-1 col-xs-1 col-sm-1"></div>
<div class="col-md-10 col-xs-10 col-sm-10">
<h3 class="round3" style="text-align:center;">Posts</h3>
</div>
<div class="col-md-1 col-xs-1 col-sm-1"></div>
</div>
<div class="row">
<div class="col-md-10 col-xs-10 col-sm-10"></div>
<div class="col-md-2 col-xs-1 col-sm-1">
<br/>
<a href="{% url 'post' %}">
<button type="button" class="btn btn-success">
<span class="glyphicon glyphicon-plus"></span>
</button>
</a>
</div>
</div>
<br/>
{% for post in post_list %}
<div class="row">
<div class="col-md-1 col-xs-1 col-sm-1"></div>
<div class="col-md-7 col-xs-7 col-sm-7">
<ul class="list-group">

<li class="list-group-item ">
<a href="{% url 'detail' post.pk %}">{{ post.title }}</a> <span class="badge"></span>
</li>
</ul>
<br>
</div>
<div class="col-md-1 col-xs-1 col-sm-1">
<a href="{% url 'detail' post.pk %}">
<button type="button" class="btn btn-info">
<span class="glyphicon glyphicon-open"></span>
</button>
</a>
</div>
<div class="col-md-1">
<a href="{% url 'edit' post.pk %}">
<button type="button" class="btn btn-info">
<span class="glyphicon glyphicon-pencil"></span>
</button>
</a>
</div>
<div class="col-md-1">
<a href="{% url 'delete' post.pk %}">
<button type="button" class="btn btn-danger">
<span class="glyphicon glyphicon-trash"></span>
</button>
</a>
</div>
<div class="col-md-1 col-xs-1 col-sm-1"></div>
</div>
{% endfor %}
</div>
{% endblock %}

This index.html page is where our posts are being rendered in a list. We extend or inherit form the base.html file.

8. Open the post-detail.html file (~/django_projects/CRUD/Crud/templates/Crud/post-detail.html) with a text editor and paste the following codes:

{% extends 'Crud/base.html' %}
{% block content %}
<div class="container-fluid">
<div class="row"></div>
<br/>
<div class="row">
<div class="col-md-1 col-xs-1 col-sm-1">
</div>
<div class="col-md-8 col-xs-8 col-sm-8">
<p class="h2">
{{post.title}}
</p>
</div>
<div class="col-md-1 col-xs-1 col-sm-1">
<a href="{% url 'edit' post.pk %}">
<button type="button" class="btn btn-info">
<span class="glyphicon glyphicon-pencil"></span>
</button>
</a>
</div>
<div class="col-md-1 col-xs-1 col-sm-1">
<a href="{% url 'delete' post.pk %}">
<button type="button" class="btn btn-danger">
<span class="glyphicon glyphicon-trash"></span>
</button>
</a>
</div>
<div class="col-md-1 col-xs-1 col-sm-1"></div>
</div>
<div class="row">
<div class="col-md-1 col-xs-1 col-sm-1"></div>
<div class="col-md-10 col-xs-10 col-sm-10">
{{post.message}}
</div>
<div class="col-md-1 col-xs-1 col-sm-1"></div>
</div>
</div>
{% endblock %}

Before we add other pages, we will be using django-widget-tweaks so let’s go ahead and install it.

9. In your terminal, run the following command:

$ pip install django-widget-tweaks

10. Now that we have django-widget-tweaks installed, let us add it to the INSTALLED_APPS. Head to the settings.py file and open it with a text editor. Scroll down to the INSTALLED_APPS section and add widget_tweaks. You should have something the below:

Let’s continue with our templates.

10. Open the post.html file (~/django_projects/CRUD/Crud/templates/Crud/post.html) with a text editor and paste the following codes:

{% load widget_tweaks %}
<!DOCTYPE html>
<html>
<head>
<title>Posts</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous">
<style type="text/css">
<style>
</style>
</style>

</head>
<body>
<div class="container-fluid">
<div class="row">
<div class="col-md-1 col-xs-1 col-sm-1"></div>

<div class="col-md-10 col-xs-10 col-sm-10 ">
<br/>
<h6 style="text-align:center;"><font color="red"> All fields are required</font> </h6>
</div>
<div class="col-md-1 col-xs-1 col-sm-1">
</div>
</div>
<div class="row">
<div class="col-md-1 col-xs-1 col-sm-1"></div>
<div class="col-md-10 col-xs-10 col-sm-10">
<form method="post" novalidate>
{% csrf_token %}
{% for hidden_field in form.hidden_fields %}
{{ hidden_field }}
{% endfor %}
{% for field in form.visible_fields %}
<div class="form-group">
{{ field.label_tag }}
{% render_field field class="form-control" %}
{% if field.help_text %}
<small class="form-text text-muted">{{ field.help_text }}</small>
{% endif %}
</div>
{% endfor %}
<button type="submit" class="btn btn-primary">post</button>
</form>
<br>
</div>
<div class="col-md-1 col-xs-1 col-sm-1"></div>
</div>
</div>
<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></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>

11. How about when we want to edit a post? Open the edit.html file (~/django_projects/CRUD/Crud/templates/Crud/edit.html) with a text editor and paste the following codes:

{% load widget_tweaks %}
<!DOCTYPE html>
<html>
<head>
<title>CRUD app</title>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.3/css/bootstrap.min.css" integrity="sha384-MCw98/SFnGE8fJT3GXwEOngsV7Zt27NXFoaoApmYm81iuXoPkFOJwJ8ERdknLPMO" crossorigin="anonymous">
<style type="text/css"></style>
</head>
<body>
<div class="container-fluid">
<div class="row">
<div class="col-md-1 col-xs-1 col-sm-1"></div>
<div class="col-md-10 col-xs-10 col-sm-10 ">
<br/>
<h6 style="text-align:center;"><font color="red"> All fields are required</font> </h6>
</div>
<div class="col-md-1 col-xs-1 col-sm-1">
</div>
</div>
<div class="row">
<div class="col-md-1 col-xs-1 col-sm-1"></div>
<div class="col-md-10 col-xs-10 col-sm-10">
<form method="post" novalidate>
{% csrf_token %}
{% for hidden_field in form.hidden_fields %}
{{ hidden_field }}
{% endfor %}
{% for field in form.visible_fields %}
<div class="form-group">
{{ field.label_tag }}
{% render_field field class="form-control" %}
{% if field.help_text %}
<small class="form-text text-muted">{{ field.help_text }}</small>
{% endif %}
</div>
{% endfor %}
<button type="submit" class="btn btn-primary">submit</button>
</form>
<br>
</div>
<div class="col-md-1 col-xs-1 col-sm-1"></div>
</div>
</div>
<script src="https://code.jquery.com/jquery-3.3.1.slim.min.js" integrity="sha384-q8i/X+965DzO0rT7abK41JStQIAqVgRVzpbzo5smXKp4YfRvH+8abtTE1Pi6jizo" crossorigin="anonymous"></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>

12. Let us now add a page to be able to delete a post. Open the confirm_delete.html file (~/django_projects/CRUD/Crud/templates/Crud/confirm_delete.html) with a file editor and paste the following codes:

{% extends 'Crud/base.html' %}
{% block content %}
<div class="container">
<div class="row"></div>
<br/>
<div class="row">
<div class="col-md-2 col-xs-2 col-sm-2"></div>
<div class="col-md-10 col-xs-10 col-sm-10">
<form method="post">
{% csrf_token %}
<div class="form-row">
<div class="alert alert-warning">
Are you sure you want to delete {{ object }}?
</div>
</div>
<button type="submit" class="btn btn-danger">
<span class="glyphicon glyphicon-trash"></span>
</button>
</form>
</div>
</div>
</div>
{% endblock %}

That was a lot!! But we are done now! Let us reep the fruits of our hard work :) Run the development server with

$ python manage.py runserver

Navigate to http://127.0.0.1:8000/ in your browser and you should see the beautiful frontend of our application.

Clicking on the + sign and it will give you a page like below:

Fill the form and click on post to add a post.

You can also click on the pencil sign in front of a post to edit or update it.

The edit page of a sample post will look like the one below:

Remove or add some text in the fields and click on submit to update the post.

And, of course, you can click on the thrash sign to delete a post.

Conclusion

We have succeeded in creating the views for our app. We have also been able to create the templates for each view and we have linked them with the urls. We shall be taking our project live in the future. But before we do, we will be using django-rest-framework to build an API for this same application. We will also be using a Frontend framework like React or Angular to interact with the API to perform these same CRUD activities. Leave a comment below and I will be happy to read and reply to them. See next tutorial on the API here

John Bagiliko

Hi there! I am John Bagiliko, Ph.D. Student in Data Science. I use Python for handling data and for software development purposes