Building a To-Do List App with Django

Building a To-Do List App with Django

To-do list applications are essential tools that improve our productivity and help us keep track of important tasks. In this tutorial, we will go through the steps involved in building a to-do list application using Django. Django is a high-level web framework that enables developers to build robust web applications quickly.

Prerequisites

It is assumed that you have prior knowledge of Python, Django, and HTML/CSS. If you are new to Django, you should take a look at the official Django Documentation to get started.

Setting up a new Django Project

First, we need to create a new Django project. Open your terminal and navigate to the directory where you want to create your project. Then run the following command:

django-admin startproject todo

The command above will create a new Django project named "todo." Once the project is created, navigate into the project directory by running:

cd todo

Next, create a new Django app for handling the to-do list application by running the following command:

python manage.py startapp todos

Creating the To-Do Model

The model is the backbone of a Django application. It defines the data structure for the application. In our case, we want to create a Todo model that has the following fields:

  • title A short description of the task.

  • description A detailed description of the task.

  • completed A checkbox that indicates if the task is completed or not.

  • created_at The date and time when the task was created.

Open the models.py file inside the todos app directory and define the Todo model as follows:

from django.db import models

class Todo(models.Model):
    title = models.CharField(max_length=100)
    description = models.TextField()
    completed = models.BooleanField(default=False)
    created_at = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return self.title

The __str__ method is used to display the model's title in the Django admin panel.

Once you have defined the model, apply the migrations by running the following command:

python manage.py makemigrations
python manage.py migrate

Creating Views and Templates

Now that we have defined the model, we can create the views and templates for our to-do list application.

Open the views.py file inside the todos app directory and define the following views:

from django.shortcuts import render, redirect
from .models import Todo

def index(request):
    todos = Todo.objects.order_by('-created_at') # get all todos ordered by newest to oldest
    return render(request, 'todos/index.html', {'todos': todos})

def create(request):
    if request.method == 'POST':
        title = request.POST['title']
        description = request.POST['description']
        completed = False if not request.POST.get('completed', False) else True
        Todo.objects.create(title=title, description=description, completed=completed)
        return redirect('todos:index')
    else:
        return render(request, 'todos/create.html')

def update(request, id):
    todo = Todo.objects.get(id=id)
    if request.method == 'POST':
        title = request.POST['title']
        description = request.POST['description']
        completed = False if not request.POST.get('completed', False) else True
        todo.title = title
        todo.description = description
        todo.completed = completed
        todo.save()
        return redirect('todos:index')
    else:
        return render(request, 'todos/update.html', {'todo': todo})

def delete(request, id):
    todo = Todo.objects.get(id=id)
    todo.delete()
    return redirect('todos:index')

The index view retrieves all the Todo objects from the database and orders them by the newest to oldest. The create view creates a new Todo object in the database while the update view updates an existing Todo object. The delete view deletes a Todo object from the database.

Next, create the templates inside the todos/templates/todos directory as follows:

index.html

{% extends 'base.html' %}

{% block content %}
<h2>To-do List</h2>

<a href="{% url 'todos:create' %}" class="btn btn-primary">Add New</a>

<ul>
    {% for todo in todos %}
    <li>
        <h4>{{ todo.title }}</h4>
        <p>{{ todo.description }}</p>
        {% if todo.completed %}
        <p>Completed: Yes</p>
        {% else %}
        <p>Completed: No</p>
        {% endif %}
        <p>Created At: {{ todo.created_at }}</p>
        <div class="btn-group" role="group">
            <a href="{% url 'todos:update' todo.id %}" class="btn btn-secondary"><i class="fas fa-edit"></i></a>
            <a href="{% url 'todos:delete' todo.id %}" class="btn btn-danger"><i class="fas fa-trash"></i></a>
        </div>
    </li>
    {% empty %}
    <h3>No todos</h3>
    {% endfor %}
</ul>
{% endblock %}

create.html

{% extends 'base.html' %}

{% block content %}
<h2>Create New Todo</h2>

<form method="POST">
    {% csrf_token %}
    <div class="form-group">
        <label for="title">Title</label>
        <input type="text" class="form-control" name="title" required>
    </div>
    <div class="form-group">
        <label for="description">Description</label>
        <textarea class="form-control" name="description" required></textarea>
    </div>
    <div class="form-group form-check">
        <input type="checkbox" class="form-check-input" name="completed">
        <label class="form-check-label" for="completed">Completed</label>
    </div>
    <button type="submit" class="btn btn-primary">Add</button>
</form>
{% endblock %}

update.html

{% extends 'base.html' %}

{% block content %}
<h2>Update Todo</h2>

<form method="POST">
    {% csrf_token %}
    <div class="form-group">
        <label for="title">Title</label>
        <input type="text" class="form-control" name="title" value="{{ todo.title }}" required>
    </div>
    <div class="form-group">
        <label for="description">Description</label>
        <textarea class="form-control" name="description" required>{{ todo.description }}</textarea>
    </div>
    <div class="form-group form-check">
        <input type="checkbox" class="form-check-input" name="completed" {% if todo.completed %}checked {% endif %}>
        <label class="form-check-label" for="completed">Completed</label>
    </div>
    <button type="submit" class="btn btn-primary">Update</button>
</form>
{% endblock %}

base.html

<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>To-do List</title>
        <!-- Bootstrap CSS -->
        <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.3.1/css/bootstrap.min.css" integrity="sha384-ggOyR0iXCbMQv3Xipma34MD+dH/1fQ784/j6cY/iJTQUOhcWr7x9JvoRxT2MZw1T" crossorigin="anonymous">
        <!-- Font Awesome -->
        <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css" integrity="sha512-7gxyERfRjHmjZ/AzP9nUbP6I246U6xEGw6mvhJdMl2hNKoFq3sW8BvC1K/Rmd1hcKYgRLE+Sh8bKwIepnMCzvg==" crossorigin="anonymous" referrerpolicy="no-referrer" />
    </head>
    <body>
        <div class="container mt-5">
            {% block content %}
            {% endblock %}
        </div>
        <!-- Bootstrap JS -->
        <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://cdn.jsdelivr.net/npm/@popperjs/core@2.9.3/dist/umd/popper-core.min.js" integrity="sha384-PXrm7r0ZtFcNlIoACWDgcQpwjKTA+vGyQdnbBoz3hG65sXsDvZjRdsiOokhTccWn" crossorigin="anonymous"></script>
        <script src="https://cdn.jsdelivr.net/npm/bootstrap@5.1.0/dist/js/bootstrap.min.js" integrity="sha384-O7vZQNf2+j3oIZXsW9GjDrURlbfAdRnBLT50obdWgEQhPhv61erg+vjJzfdmX9BZ" crossorigin="anonymous"></script>
    </body>
</html>

Registering the App

Now that we have created the views and templates, we need to register the app in the project's settings.py file. Open the settings.py file and add the following to the INSTALLED_APPS list:

INSTALLED_APPS = [
    # ...
    'todos',
]

Hooking up the URLs

Finally, we need to add the URLs for our to-do list application. Open the urls.py file inside the todos app directory and add the following:

from django.urls import path
from . import views

app_name = 'todos'

urlpatterns = [
    path('', views.index, name='index'),
    path('create/', views.create, name='create'),
    path('update/<int:id>/', views.update, name='update'),
    path('delete/<int:id>/', views.delete, name='delete'),
]

The URLs above map the application views to their respective URLs.

Try it out

That's it! We have now created a to-do list application using Django. To test the application, run the development server by executing the following command:

python manage.py runserver

You can access the to-do list application by visiting http://localhost:8000/todos/ in your web browser.

Conclusion

In this tutorial, we went through the process of building a to-do list application using Django. We covered the creation of the model, views, and templates, as well as registering the app and hooking up the URLs.

This application is just a starting point, and there are many ways to extend and customize it. For example, you can add authentication, pagination, search functionality, or even deploy the application to the cloud for public use. Try experimenting with different features and learn more about Django to build more robust web applications!