Python Django Projects | Create a Todo Web Application
In this tutorial, you’ll learn to create Todo Web Application using Python Django Framework.
After you complete this tutorial you’ll acquire below knowledge when working with Django.
- Configuring MySql database.
- Specifying template paths properly.
- Basics of Template inheritance.
- Creating views using class-based views.
- Create a model form using customizing field widgets and properties
- Displaying a flash message on operation success or failure using messages module.
Create Virtual Environment
Install environment using Python PIP package manager.
pip install virtualenv
Confirm installation by outputting virtualenv version.
virtualenv --version
Below command creates a virtual environment from your project. The env
is simply a folder name. You can give it any name you like.
virtualenv env
For a more in-depth understanding of the virtual environment, you can visit this guide.
Now you must activate that env source
using below command.
source env/bin/activate
We’ll be using MySql database for storing tasks so install mysqlclient
for Django application.
(env) root@root-X541UJ:~/python$ pip install mysqlclient
Create a Django Project
After activating env source
the file you will see (env)
on terminal like this (env) root@root-X541UJ:~/python/
.
Only after this step, you must create a new Django project using below command.
(env) root@root-X541UJ:~/python$ django-admin startproject todo
For creating a new Django app.
(env) root@root-X541UJ:~/python$ django-admin startapp todo_maker
Configure Setting.py file
In todo/settings.py
# Register app INSTALLED_APPS = [ ... 'todo_maker.apps.TodoMakerConfig', ] # Connect to MySQL database DATABASES = { 'default': { 'ENGINE': 'django.db.backends.mysql', 'NAME': 'django_todo', 'USER': 'root', 'PASSWORD': '', 'HOST': 'localhost', # Or an IP Address that your DB is hosted on 'PORT': '3306', 'OPTIONS': { 'sql_mode': 'traditional', } } } # Make sure django finds all the templates in TEMPLATES list 'DIRS': [ os.path.join(BASE_DIR, 'todo/templates/todo'), os.path.join(BASE_DIR, 'templates'), ],
Include the todo_maker
app in todo/urls.py
.
from django.contrib import admin from django.urls import path, include urlpatterns = [ path('', include('todo_maker.urls')), path('admin/', admin.site.urls), ]
Setup base template for inheritance
In todo/templates/todo/base.html
create 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"> <title>Django Todo Application</title> {% block stylesheets %} {% endblock %} </head> <body> {% block content %} {% endblock %} </body> </html>
Making Models for Todo App
In todo_maker/models.py
.
from django.db import models from datetime import datetime # Create your models here. class Task(models.Model): id = models.AutoField(primary_key=True) title = models.CharField(max_length=50) description = models.TextField(null=True) task_accomplished = models.IntegerField(default=0) created_at = models.DateTimeField(auto_now_add=datetime.now(), null=True) updated_at = models.DateTimeField(auto_now=datetime.now(), null=True) class Meta: db_table = "tasks" verbose_name = "Task" verbose_name_plural = "Tasks" def __str__(self): return "ID = {}, title = {}".format(self.description, self.title)
The model Task
is used to store task information given by the user. The task_accomplished
is an integer field which takes two values 1 means task completed or 0 means task pending.
Now create a migration file for the Task
model.
(env) root@root-X541UJ:~/python$ python manage.py makemigrations todo_maker
Use below command to create task table in the database.
(env) root@root-X541UJ:~/python$ python manage.py migrate
Preview of Database Tables
Add routes for listing, creation, deletion and task completion of a task
In todo_maker/urls.py
.
from django.urls import path from todo_maker import views app_name = "todo_maker" urlpatterns = [ path('', views.List.as_view(),name="list-todo"), path('save', views.List.as_view(),name="save-todo"), path('edit/', views.List.as_view(),name="edit-todo"), path('remove/', views.List.as_view(),name="remove-todo"), path('task-completed', views.List.as_view(),name="todo-task-completed"), ]
Create ModelForm to save and update the task object
In todo_maker/forms.py
.
from django.forms import Form, ModelForm from django import forms from todo_maker.models import Task from django.utils.safestring import mark_safe class TaskCreateForm(ModelForm): id = forms.CharField(required=False) task_accomplished = forms.ChoiceField( widget = forms.Select(attrs={'class': "form-control"}), choices=[(0, "Task Pending"), (1, "Task Accomplished")], ) class Meta: model = Task fields = ("id", "title", "description", "task_accomplished",) labels = { "title" : mark_safe("Task Title {}".format("<span class='required' >*</span>")), "task_accomplished" : mark_safe("Task accomplished {}".format("<span class='required' >*</span>")), } widgets = { "title" : forms.TextInput(attrs={'class' : 'form-control'}), "description" : forms.Textarea(attrs={'class' : 'form-control', 'rows' : 2}), } def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs)
Here TaskCreateForm
uses ModelForm
and the referred model is Task
a model. Here title, task_accomplished fields are mandatory field.
Prepare Views for Todo App
In todo_maker/views.py
.
from django.shortcuts import render, redirect from django.http import HttpResponse from django.views import View from todo_maker.models import Task from todo_maker.forms import TaskCreateForm from django.contrib import messages from django.urls import reverse # Create your views here. class List(View): template = "todo_maker/list.html" def get(self, request, *args, **kwargs): instance = None if kwargs.get('pk', None): try: instance = Task.objects.filter(pk=kwargs.get('pk', None))[0] except Exception: instance = None delete_id = kwargs.get('delete_id', None) if delete_id: try: obj = Task.objects.filter(pk=delete_id)[0] obj.delete() messages.add_message(request, messages.SUCCESS, "Task removed successfully.") except Exception: pass form = TaskCreateForm(instance=instance) context = { "tasks" : Task.objects.filter().order_by('task_accomplished'), "form" : form, } return render(request, self.template, context) def post(self, request, *args, **kwargs): instance = None post = request.POST.copy() pk = post.get('id', None) msg = "New Task Created..." if pk: try: msg = "Task Updated..." instance = Task.objects.filter(pk=pk)[0] except Exception: instance = None form = TaskCreateForm(post, instance=instance) if form.is_valid(): instance = form.save(commit=False) instance.save() messages.add_message(request, messages.SUCCESS, msg) else: messages.add_message(request, messages.ERROR, "Please fill * marked fields") url = reverse("todo_maker:list-todo") return redirect(url)
In form = TaskCreateForm(instance=instance)
the instance is the task object that is used for populating TaskCreateForm.
Here get() method of ListView performs two functions they are :
- It displays the list of tasks in the first task pending order.
- If pk value is passed then it populates the form with task data of given task id.
Also, the post() method of ListView performs two functions of saving and updating form data passed to TaskCreateForm
.
Create Templates
In todo_maker/templates/todo_maker/list.html
.
{% extends "base.html" %} {% block stylesheets %} {{ block.super }} <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.4.1/css/bootstrap.min.css"> <style> body{ margin: 0; padding: 0; background-color: #009688; } .container-fluid > .wrapper{ width: 50%; margin: auto; padding-top: 50px; padding-bottom: 50px; background-color: #ffffff; } .title-container{ background-color: aliceblue; padding: 10px 0; } .title-container h1{ margin: 0; text-align: center; } .create-task-container, .list-task-container{ padding: 15px 25px 15px 25px; } .message-container{ margin-top: 15px; } .required{ color: red; } </style> {% endblock %} {% block content %} <div class="container-fluid"> <div class="wrapper"> <div class="title-container"> <h1>Django Todo Application</h1> </div> {% if messages %} <div class="message-container"> {% for message in messages %} {% if message.level == DEFAULT_MESSAGE_LEVELS.SUCCESS %} <div class="alert border-0 alert-success mar-bot-10-imp alert-dismissible fade show border-radius-none" role="alert"> <div id="success-notification-div"> {{ message }} </div> <button type="button" class="close" data-dismiss="alert" aria-label="Close"> <i class="ti ti-close"></i> </button> </div> {% endif %} {% if message.level == DEFAULT_MESSAGE_LEVELS.ERROR %} <div class="alert border-0 alert-danger mar-bot-10-imp alert-dismissible fade show border-radius-none" role="alert"> <div id="danger-notification-div"> {{ message }} </div> <button type="button" class="close" data-dismiss="alert" aria-label="Close"> <i class="ti ti-close"></i> </button> </div> {% endif %} {% endfor %} </div> {% endif %} <div class="create-task-container"> <h2 class="">Create Task</h2> <form action="{% url 'todo_maker:save-todo' %}" method="post"> {% csrf_token %} {{ form.id.as_hidden }} <div class="form-group"> {{form.title.label}} {{form.title}} </div> <div class="form-group"> {{form.description.label}} {{form.description}} </div> <div class="form-group"> {{form.task_accomplished.label}} {{form.task_accomplished}} </div> <div class="form-group"> <input type="submit" class="btn btn-primary" value="Save"> </div> </form> </div> <div class="list-task-container"> <h2 class="">List Tasks</h2> <table border="1" class="task-table table table-bordered" > <thead> <tr> <td>Title</td> <td>Description</td> <td>Action</td> </tr> </thead> <tbody> {% if tasks %} {% for task in tasks %} <tr> <td> {{ task.title }} {% if task.task_accomplished == 1 %} <span class="badge badge-success">Task Completed</span> {% endif %} </td> <td>{{ task.description }}</td> <td> <a href="{% url 'todo_maker:edit-todo' pk=task.pk %}" class="btn btn-info btn-sm">Edit</a> <a href="{% url 'todo_maker:remove-todo' delete_id=task.pk %}" class="btn btn-danger btn-sm">Remove</a> </td> </tr> {% endfor %} {% else %} <tr> <td colspan="3">No task available</td> </tr> {% endif %} </tbody> </table> </div> </div> </div> {% endblock %}
For the remainder, base.html is imported from a folder todo/templates/src/
.
Run Django Todo Application
(env) root@root-X541UJ:~/python/todo$ python manage.py runserver Watching for file changes with StatReloader Performing system checks... System check identified no issues (0 silenced). May 08, 2020 - 13:21:44 Django version 2.2.7, using settings 'todo.settings' Starting development server at http://127.0.0.1:8000/ Quit the server with CONTROL-C.
Todo Application Final Output
Video Preview
Conclusion
In conclusion, we have come to an end. For more information on Django comment below and if you would like to explore more about Django then you can visit Official Django Website.




