Django project - TODO App


A TODO app is a simple tool that helps you keep track of tasks you need to complete.

It allows you to add, manage, and mark tasks as done, keeping you organized and on top of your responsibilities.

Github Repo Link


What we will create

  • Creating Project and App: We start by setting up a Django project and creating a specific app within it to handle our TODO tasks.

  • Creating Database: Django automatically sets up a database when you create a project. We'll define models to store our TODO items, which will be saved and retrieved from this database.

  • Creating Forms: We'll create forms to add and update TODO items, making it easy for users to input their tasks. These forms will be tied to our models, ensuring data consistency and validation.

  • CRUD Operations: CRUD stands for Create, Read, Update, and Delete—the core operations we'll implement for managing TODO items. This lets users add new tasks, view them, make changes, and delete tasks they no longer need.

  • Move Around TODO Item on Window: This feature allows users to drag and rearrange their TODO items, helping them prioritize tasks visually. It's a simple yet effective way to keep tasks in order.

  • Move to Bin: Users can move tasks they no longer need into a "bin" or trash, where they can either be permanently deleted or restored later. This adds a layer of safety before completely removing a task.


How to Create a Django Project 

To create a Django project, you'll need to start by installing Django and setting up a new project.

This involves using the django-admin startproject command followed by the name of your project.

For a detailed guide, you can follow this existing blog, we are assuming you have given name o project as todoProject How to Create a Django Project.

make sure you have reach to running server properly.

Missed Something? check this github code


How to Create an App Inside a Django Project

Once your Django project is set up, you can create an app within it using the python manage.py startapp command followed by the name of your app. if server is still running stop it. make sure to have virtual env activated.

To create a TODO app, you would run. 

cd todoProject
python manage.py startapp todo

This command will generate a directory structure with the necessary files to start building the app.

Missed Something? check this github code


Creating the Database

In Django, models are Python classes that represent database tables.

Each attribute of the model corresponds to a field in the table, allowing you to define the structure of your data directly in code.

For our TODO list, we'll create a model to store each task.

Here’s how you can define the TodoItem model in your models.py file

from django.db import models

class TodoItem(models.Model):
    title = models.CharField(max_length=100)
    description = models.TextField(blank=True, null=True)
    completed = models.BooleanField(default=False)

    def __str__(self):
        return self.title

After defining the model, you need to register your app in the INSTALLED_APPS list within settings.py

INSTALLED_APPS = [
    # other apps
    'todo',  # add this line to register your todo app
]

Next, run the following commands to create the necessary database tables

python manage.py makemigrations
python manage.py migrate

These commands will generate and apply migrations, creating the database structure based on your models.

Missed Something? check this github code


Register Model to Admin Site

To manage your TODO items through Django's admin interface, you need to register the TodoItem model with the admin site.

This allows you to create, update, and delete tasks directly from the admin panel.

  • Open the admin.py file in your todo app directory.
  • Add the following code to register the TodoItem model 
    from django.contrib import admin
    from .models import TodoItem
    
    @admin.register(TodoItem)
    class TodoItemAdmin(admin.ModelAdmin):
        list_display = ('title', 'description', 'completed')
        list_filter = ('completed',)
        search_fields = ('title', 'description')
    

This setup does the following:

  • list_display: Specifies the fields to be displayed in the admin list view.
  • list_filter: Adds filters in the sidebar to filter tasks by completion status.
  • search_fields: Allows searching for tasks by title or description.

With this setup, you'll have full control over your TODO items from the Django admin interface. Click on add, create few todos from admin.

Missed Something? check this github code


Creating a Form to Input TODO Items

To allow users to input TODO items and save them to the database, we'll create a Django form.

This form will be tied to our TodoItem model, making it easy to validate and save data.

Steps to create the form:

  1. Create a forms.py file: If it doesn't already exist, create a forms.py file inside your todo app directory.

  2. Define the form: Add the following code to forms.py

from django import forms
from .models import TodoItem

class TodoItemForm(forms.ModelForm):
    class Meta:
        model = TodoItem
        # Excluded completed
        fields = ['title', 'description']
  • ModelForm: We're using ModelForm to automatically create a form based on the TodoItem model.
  • fields: Specifies which fields to include in the form.
  • widgets: Customizes the form widgets, like setting placeholders and using a checkbox for the completed field.

Missed Something? check this github code


Creating Views to Render the Form (CBV and FBV)

In this step, we'll create two types of views to handle the TODO form: a Class-Based View (CBV) and a Function-Based View (FBV).

Both will render the form and allow users to submit tasks.

Function-Based View (FBV)

First, let's create a function-based view to handle the form

  • Open or create the views.py file in your todo app directory.
  • Add the following code 
    from django.shortcuts import render, redirect
    from .forms import TodoItemForm
    
    def todo_create(request):
        if request.method == 'POST':
            form = TodoItemForm(request.POST)
            if form.is_valid():
                form.save()
                return redirect('todo:todo_list')  # Redirect to the list view after saving
        else:
            form = TodoItemForm()
        
        return render(request, 'todo/todo_form.html', {'form': form})
    

Class-Based View (CBV)

Now, let's create a class-based view for the same purpose

  • Add the following code to views.py
    from django.views.generic.edit import CreateView
    from django.urls import reverse_lazy
    from .models import TodoItem
    
    class TodoCreateView(CreateView):
        model = TodoItem
        template_name = 'todo/todo_form.html'
        fields = ['title', 'description']
        success_url = reverse_lazy('todo_list')  # Redirect to the list view after saving
    

Setting Up the Template

Next, we need to create the template that both views will use to render the form

  • Create a directory named templates inside your todo app directory.
  • Inside templates, create another directory named todo.
  • Create a file named todo_form.html inside the todo directory and add the following code
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Create TODO</title>
</head>
<body>
    <h2>Create a New TODO Item</h2>
    <form method="post">
        {% csrf_token %}
        {{ form.as_p }}
        <button type="submit">Save Task</button>
    </form>
</body>
</html>


URL Configuration

Finally, let's set up the URLs to access these views

  • Lets create urls.py file inside app and then use it to register to main urls.py file
  • Add the following code.
    from django.urls import path
    from .views import todo_create, TodoCreateView
    
    urlpatterns = [
        path('create/', todo_create, name='todo_create'),  # FBV
        path('create-cbv/', TodoCreateView.as_view(), name='todo_create_cbv'),  # CBV
    ]
    
  • Open the urls.py file in your project directory and register our app urls file to it.
    from django.contrib import admin
    from django.urls import path, include
    
    urlpatterns = [
        path('admin/', admin.site.urls),
        path('', include(('todo.urls','todo'), namespace='todo')),
    ]
     
  • FBV (Function-Based View): Handles form submission and rendering through a traditional function.
  • CBV (Class-Based View): Utilizes Django's built-in CreateView for a more abstract and reusable approach.
  • Template: Both views use the same todo_form.html template to render the form.

This setup gives users flexibility in choosing between FBV and CBV while showcasing Django’s capabilities.

Missed Something? check this github code

Todo App - Create Form


Displaying the Created TODO Items

Now that users can create TODO items, let's create a view to display them in a list.

We'll set up a basic template that shows all TODO items, separated by an <hr> tag for clarity.

Create the View to List TODO Items

In the views.py file, add the following function-based view

from django.shortcuts import render
from .models import TodoItem

def todo_list(request):
    todos = TodoItem.objects.all()  # Fetch all TODO items from the database
    return render(request, 'todo/todo_list.html', {'todos': todos})

If you want to use a class-based view instead, you can use Django's built-in ListView

from django.views.generic import ListView
from .models import TodoItem

class TodoListView(ListView):
    model = TodoItem
    template_name = 'todo/todo_list.html'
    context_object_name = 'todos'  # Use 'todos' in the template to access the list of items

Set Up the Template to Display the TODO List

Create a new template file named todo_list.html in the todo directory inside templates

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>TODO List</title>
</head>
<body>
    <h2>TODO List</h2>
    {% for todo in todos %}
        <h3>{{ todo.title }}</h3>
        <p>{{ todo.description }}</p>
        <p>Completed: {{ todo.completed }}</p>
        <hr>
    {% empty %}
        <p>No TODO items yet!</p>
    {% endfor %}
</body>
</html>

URL Configuration

Finally, let's set up the URLs to access these views:

  • Open the urls.py file in your project directory (if it doesn't exist, create it).
  • Add the following code 
    from django.urls import path
    from .views import todo_list, TodoListView
    
    urlpatterns = [
        path('', todo_list, name='todo_list'),  # FBV for listing TODO items
        path('', TodoListView.as_view(), name='todo_list_cbv'),  # CBV for listing TODO items
    ]
    
  • Template: The todo_list.html template loops through the TODO items and displays each one with a title, description, and completion status. Each item is separated by an <hr> tag.
  • FBV & CBV: We provided both function-based and class-based views to list the TODO items, allowing you to choose the approach that best fits your needs.

This setup will show all the TODO items in a simple, clean list format.

if you start your server with following comand you should see following page.

# inside TODO-Project\todoProject and keeping virtual env activated
python manage.py runserver

Missed Something? check this github code

Todo App - Display Items


Creating an Update View for TODO Items

Now, we'll add functionality to update existing TODO items. We'll create both a function-based view (FBV) and a class-based view (CBV) for this purpose.

Then, we'll update the TODO list template to include an "Edit" link for each item.

Function-Based View (FBV) for Updating TODO Items

Add the following function-based view to views.py

from django.shortcuts import get_object_or_404

def todo_update(request, pk):
    todo = get_object_or_404(TodoItem, pk=pk)  # Fetch the TODO item by its primary key
    if request.method == 'POST':
        form = TodoItemForm(request.POST, instance=todo)  # Bind the form to the existing TODO item
        if form.is_valid():
            form.save()
            return redirect('todo_list')  # Redirect to the list view after updating
    else:
        form = TodoItemForm(instance=todo)  # Prepopulate the form with the existing TODO item data

    return render(request, 'todo/todo_form.html', {'form': form})

Class-Based View (CBV) for Updating TODO Items

Add the following class-based view to views.py

from django.views.generic.edit import UpdateView

class TodoUpdateView(UpdateView):
    model = TodoItem
    template_name = 'todo/todo_form.html'
    fields = ['title', 'description', 'completed']
    success_url = reverse_lazy('todo:todo_list')  # Redirect to the list view after updating

Template for Update Form

We'll use the same todo_form.html template we created earlier for both creating and updating TODO items. No changes are needed in the template file.

Update URL Configuration

Update your urls.py file to include the new views for updating TODO items

from django.urls import path
from .views import todo_update, TodoUpdateView

urlpatterns = [
    path('update/<int:pk>/', todo_update, name='todo_update'),  # FBV for updating TODO
    path('update-cbv/<int:pk>/', TodoUpdateView.as_view(), name='todo_update_cbv'),  # CBV for updating TODO
]

Update TODO List Template for Edit Link

Finally, update the todo_list.html template to include an "Edit" link for each TODO item

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>TODO List</title>
</head>
<body>
    <h2>TODO List</h2>
    {% for todo in todos %}
        <h3>{{ todo.title }}</h3>
        <p>{{ todo.description }}</p>
        <p>Completed: {{ todo.completed }}</p>
        {% if not todo.completed %}<a href="{% url 'todo:todo_update' todo.pk %}">Edit</a>  <!-- FBV Edit Link -->{% endif %}
        {% if not todo.completed %}<a href="{% url 'todo:todo_update_cbv' todo.pk %}">Edit (CBV)</a>  <!-- CBV Edit Link -->{% endif %}
        <hr>
    {% empty %}
        <p>No TODO items yet!</p>
    {% endfor %}
</body>
</html>
  • Update Form: We reused the todo_form.html template for both creating and updating TODO items.
  • FBV & CBV: Both function-based and class-based views were created to handle the update functionality.
  • Edit Link: The TODO list template now includes "Edit" links that direct users to the appropriate update view.

With this setup, users can easily update their TODO items using either FBV or CBV, showcasing the flexibility of Django.

Missed Something? check this github code

Todo App - Items shown



Adding Bootstrap 5 to Project

we'll install the django-bootstrap-v5 package, integrate it into your project, and update your forms and HTML templates to use Bootstrap 5 for styling.

Install the Bootstrap 5 Package

First, install the django-bootstrap-v5 package using pip

pip install django-bootstrap-v5

Configure Django to Use django-bootstrap-v5

Next, add django_bootstrap_v5 to your INSTALLED_APPS in your Django project's settings.py

INSTALLED_APPS = [
    # other apps
    'bootstrap5',
]

This package will now be available across your project, allowing you to use Bootstrap 5 components easily.

Update the Form Template

We'll modify the todo_form.html template to apply Bootstrap 5 classes to the form elements.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Create or Update TODO</title>
    {% load bootstrap5 %}
    {% bootstrap_css %}
</head>
<body>
    <div class="container mt-5">
        <h2>Create or Update a TODO Item</h2>
        <form method="post" class="mt-3">
            {% csrf_token %}
            {% bootstrap_form form %}
            <button type="submit" class="btn btn-primary mt-3">Save Task</button>
        </form>
    </div>
 {% bootstrap_javascript %}
</body>
</html>

Update the TODO List Template

Now, update the todo_list.html template to include Bootstrap styling.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>TODO List</title>
    {% load bootstrap5 %}
    {% bootstrap_css %}
</head>
<body>
    <div class="container mt-5">
        <h2 class="mb-4">TODO List</h2>
   <div id="todo-container">
        {% for todo in todos %}
             <div class="card mb-3 draggable" draggable="true" id="todo-{{ todo.pk }}">
                <div class="card-body">
                    <h5 class="card-title">{{ todo.title }}</h5>
                    <p class="card-text">{{ todo.description }}</p>
                    <p class="card-text"><small class="text-muted">Completed: {{ todo.completed }}</small></p>
                    {% if not todo.completed %}<a href="{% url 'todo:todo_update' todo.pk %}" class="btn btn-warning">Edit</a>  <!-- FBV Edit Link -->{% endif %}
                    {% if not todo.completed %}<a href="{% url 'todo:todo_update_cbv' todo.pk %}" class="btn btn-warning">Edit (CBV)</a>  <!-- CBV Edit Link -->{% endif %}
                </div>
            </div>
        {% empty %}
            <p>No TODO items yet!</p>
        {% endfor %}
</div>
    </div>
</body>
</html>

With these updates, TODO app will have a more polished, modern look, thanks to Bootstrap 5.

Missed Something? check this github code


Todo App - Bootstrap 5 Added

Todo App - Boostrap 5 based Form


Creating a base.html Template and Using Content Blocks

In this step, we'll create a base.html template to hold the common HTML structure for your pages.

We'll then update the existing templates to extend base.html and use content blocks for page-specific content.

Create base.html

First, create a new file named base.html in your templates directory. This file will contain the common structure, such as the <head> section, navigation, and footer.

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>{% block title %}TODO App{% endblock %}</title>
    {% load bootstrap5 %}
    {% bootstrap_css %}
    {% block extra_head %}
    <!-- Additional head content can be added here -->
    {% endblock %}
</head>
<body>
    <nav class="navbar navbar-expand-lg navbar-light bg-light">
        <a class="navbar-brand" href="{% url 'todo:todo_list' %}">TODO App</a>
        <div class="collapse navbar-collapse">
            <ul class="navbar-nav mr-auto">
                <li class="nav-item">
                    <a class="nav-link" href="{% url 'todo:todo_create' %}">Add TODO</a>
                </li>
            </ul>
        </div>
    </nav>

    <div class="container mt-5">
        {% block content %}
        <!-- Page-specific content goes here -->
        {% endblock %}
    </div>

    {% bootstrap_javascript %}
    {% block extra_scripts %}
    <!-- Additional scripts can be added here -->
    {% endblock %}
</body>
</html>

Implementing "Move Around TODO Item on Window" Feature

In this step, we'll add a feature that allows users to drag and move TODO items around on the screen. This will be done using a bit of JavaScript for the drag-and-drop functionality.

Update Existing Templates to Extend base.html

Now, update the todo_form.html and todo_list.html templates to extend base.html and use content blocks.

todo_form.html

{% extends 'base.html' %}
{% load bootstrap5 %}
{% block title %}Create or Update TODO{% endblock %}

{% block content %}
<h2>Create or Update a TODO Item</h2>
<form method="post" class="mt-3">
    {% csrf_token %}
    {% bootstrap_form form %}
    <button type="submit" class="btn btn-primary mt-3">Save Task</button>
</form>
{% endblock %}

todo_list.html

{% extends 'base.html' %}

{% block title %}TODO List{% endblock %}

{% block content %}
<h2 class="mb-4">TODO List</h2>
   <div id="todo-container">
        {% for todo in todos %}
 <div class="card mb-3 draggable" draggable="true" id="todo-{{ todo.pk }}">
                <div class="card-body">
                    <h5 class="card-title">{{ todo.title }}</h5>
                    <p class="card-text">{{ todo.description }}</p>
                    <p class="card-text"><small class="text-muted">Completed: {{ todo.completed }}</small></p>
                    {% if not todo.completed %}<a href="{% url 'todo:todo_update' todo.pk %}" class="btn btn-warning">Edit</a>  <!-- FBV Edit Link -->{% endif %}
                    {% if not todo.completed %}<a href="{% url 'todo:todo_update_cbv' todo.pk %}" class="btn btn-warning">Edit (CBV)</a>  <!-- CBV Edit Link -->{% endif %}
                </div>
            </div>
        {% empty %}
            <p>No TODO items yet!</p>
        {% endfor %}
</div>
{% endblock %}

{% block extra_scripts %}
<script>
    // JavaScript for drag-and-drop functionality
</script>
{% endblock %}
  • base.html: Contains the common structure and includes content blocks for title, head content, body content, and scripts.
  • {% block %}and {% extends %}These Django template tags allow the child templates to inherit the layout of base.html and inject their unique content into defined blocks.

Missed Something? check this github code

Todo App - Bootstrap 5 Navbar


Update TODO List Template for Drag-and-Drop

First, we'll update the todo_list.html template to make each TODO item draggable

{% extends 'base.html' %}

{% block title %}TODO List{% endblock %}
{% block extra_head %}
<style>
   .draggable {
       cursor: move;
   }
</style>
{% endblock extra_head %}
{% block content %}
<h2 class="mb-4">TODO List</h2>
        {% for todo in todos %}
            <div class="card mb-3">
                <div class="card-body">
                    <h5 class="card-title">{{ todo.title }}</h5>
                    <p class="card-text">{{ todo.description }}</p>
                    <p class="card-text"><small class="text-muted">Completed: {{ todo.completed }}</small></p>
                    {% if not todo.completed %}<a href="{% url 'todo:todo_update' todo.pk %}" class="btn btn-warning">Edit</a>  <!-- FBV Edit Link -->{% endif %}
                    {% if not todo.completed %}<a href="{% url 'todo:todo_update_cbv' todo.pk %}" class="btn btn-warning">Edit (CBV)</a>  <!-- CBV Edit Link -->{% endif %}
                </div>
            </div>
        {% empty %}
            <p>No TODO items yet!</p>
        {% endfor %}
{% endblock %}

{% block extra_scripts %}
<script>
    // JavaScript for drag-and-drop functionality
    const draggables = document.querySelectorAll('.draggable');
        const container = document.getElementById('todo-container');

        draggables.forEach(draggable => {
            draggable.addEventListener('dragstart', () => {
                draggable.classList.add('dragging');
            });

            draggable.addEventListener('dragend', () => {
                draggable.classList.remove('dragging');
            });
        });

        container.addEventListener('dragover', e => {
            e.preventDefault();
            const afterElement = getDragAfterElement(container, e.clientY);
            const draggable = document.querySelector('.dragging');
            if (afterElement == null) {
                container.appendChild(draggable);
            } else {
                container.insertBefore(draggable, afterElement);
            }
        });

        function getDragAfterElement(container, y) {
            const draggableElements = [...container.querySelectorAll('.draggable:not(.dragging)')];

            return draggableElements.reduce((closest, child) => {
                const box = child.getBoundingClientRect();
                const offset = y - box.top - box.height / 2;
                if (offset < 0 && offset > closest.offset) {
                    return { offset: offset, element: child };
                } else {
                    return closest;
                }
            }, { offset: Number.NEGATIVE_INFINITY }).element;
        }
</script>
{% endblock %}

Drag-and-Drop Implementation

  • Draggable Attribute: Each TODO item card has the draggable="true" attribute, making it draggable.
  • Drag Events: We add event listeners for dragstart and dragend to highlight the item being dragged.
  • Drag Over Event: This event allows other elements to move and create space when dragging an item.
  • JavaScript Functions: The functions handle the movement logic, determining where to place the dragged item based on its position relative to other items.

Missed Something? check this github code

Todo App - Drag Drop

Todo App - Drag Drop


Implementing "Move to Bin" Feature

After setting up the drag-and-drop feature, we'll move on to implementing the "Move to Bin" functionality, where users can drag TODO items to a bin icon to delete them.

Add a Bin Icon to the Template

First, let's update the todo_list.html template to include a bin icon.

<div id="bin">
      <svg xmlns="http://www.w3.org/2000/svg" width="50" height="50" fill="currentColor" class="bi bi-archive-fill" viewBox="0 0 16 16">
         <path d="M12.643 15C13.979 15 15 13.845 15 12.5V5H1v7.5C1 13.845 2.021 15 3.357 15zM5.5 7h5a.5.5 0 0 1 0 1h-5a.5.5 0 0 1 0-1M.8 1a.8.8 0 0 0-.8.8V3a.8.8 0 0 0 .8.8h14.4A.8.8 0 0 0 16 3V1.8a.8.8 0 0 0-.8-.8z"/>
       </svg>
  </div>

This bin icon will be placed in a fixed position on the screen, allowing users to drag TODO items to it.

JavaScript for Deleting Items

Add the following JavaScript code to handle the deletion of items when they are dragged over the bin

const bin = document.getElementById('bin');

bin.addEventListener('dragover', e => {
    e.preventDefault();
});

bin.addEventListener('drop', e => {
    e.preventDefault();
    const draggable = document.querySelector('.dragging');
    const todoId = draggable.id.split('-')[1];

    // Make an AJAX request to delete the item
    fetch(`/delete-todo/${todoId}/`, {
        method: 'DELETE',
        headers: {
            'X-CSRFToken': '{{ csrf_token }}',
        },
    }).then(response => {
        if (response.ok) {
            draggable.remove();  // Remove the item from the DOM
        } else {
            alert('Failed to delete TODO item.');
        }
    });
});

Delete View in Django

Now, create a view in views.py to handle the deletion of TODO items.

from django.http import JsonResponse
from django.views.decorators.http import require_http_methods

@require_http_methods(["DELETE"])
def delete_todo(request, pk):
    todo = get_object_or_404(TodoItem, pk=pk)
    todo.delete()
    return JsonResponse({'status': 'success'})

Update URL Configuration

Update your urls.py to include the new delete view.

path('delete-todo/<int:pk>/', delete_todo, name='delete_todo'),
  • Move Around Feature: Users can now drag and move TODO items around on the screen.
  • Move to Bin Feature: TODO items can be dragged to a bin icon to delete them.
  • JavaScript: We used JavaScript to implement drag-and-drop and AJAX to handle deletion without reloading the page.

This enhances the user experience by making the TODO app interactive and intuitive.

Missed Something? check this github code

Todo App - Move to bin


Exercise: Enhance Your TODO App

Now that you've built a basic TODO app with Django, here are some exercises to help you add more features and improve the project.

1. Separate JavaScript into a Dedicated File

  • Move the inline JavaScript code from your HTML templates to a separate JavaScript file (e.g., todo.js).
  • Update your templates to include the external JavaScript file using the {% static %} tag.
  • This will help keep your code organized and improve maintainability.

2. Implement Search Functionality

  • Add a search bar to the TODO list page that allows users to search for TODO items by title or description.
  • Implement the search feature in the view, filtering the TODO items based on the search query.

3. Add Pagination

  • If your TODO list grows, it can become overwhelming. Add pagination to the TODO list view to display a limited number of items per page.
  • Use Django’s built-in pagination features to achieve this.

4. Add Categories to TODO Items

  • Extend the TODO model to include a category field (e.g., Work, Personal, Urgent).
  • Allow users to filter TODO items by category on the TODO list page.

5. Enhance the UI with Bootstrap

  • Now that you’ve integrated Bootstrap, explore more of its components.
  • Add Bootstrap’s alert messages for user feedback (e.g., after creating, updating, or deleting a TODO item).
  • Style your forms, buttons, and navigation bar using Bootstrap classes.

6. Implement User Authentication

  • Add user authentication to your app so that different users can have their own TODO lists.
  • Use Django’s built-in authentication system to allow users to sign up, log in, and manage their TODOs.

7. Add Due Dates to TODO Items

  • Add a due date field to your TODO model.
  • Highlight or sort TODO items by their due date to indicate urgency.

8. Implement a Mark as Complete Feature

  • Add a checkbox or button next to each TODO item that allows users to mark it as complete.
  • Style completed TODO items differently (e.g., strikethrough text or a different background color).

9. Use Django Signals to Notify Users

  • Set up Django signals to notify users (e.g., via email) when a TODO item is due soon or overdue.
  • This will help users stay on top of their tasks.

10. Create a REST API for the TODO App

  • Implement a REST API using Django REST Framework that allows other applications to interact with your TODO app.
  • Add endpoints for creating, updating, deleting, and listing TODO items.

11. Deploy Your TODO App

  • Once you’ve added these features, try deploying your TODO app to a cloud service like AWS or pythonanywhere.
  • Ensure that your app is production-ready with proper security settings and error handling.

These exercises will give you more hands-on experience with Django and help you create a more robust and feature-rich TODO app.