Build a Todo web app with Django

·

9 min read

In this article, i will be walking you through the creation of a todo web app with django. Python should be installed on your OS. I am using VS code editor and create a folder called todo, open the folder in your code editor and run the following commands in your terminal.

python3 -m venv venv
source venv/bin/activate
pip install django

From the code snippet above, a virtual environment is used (venv), this is to prevent a clutter of django packages in your os. The virtual environment houses all the packages you will install for this project. The environment is then activated with the source command.

Use source venv/scripts/activate for Windows OS.

Next, we create a Django project

 django-admin startproject djangotodo .

Ensure you're in the directory that has the manage.py file,then create a Todo app.

 python3 manage.py startapp todo

Run python manage.py migrate and python manage.py makemigrations to apply migrations.

Add 'taskapp' to your INSTALLED_APPS in your djangotodo/settings.py

INSTALLED_APPS = [
    ...
    'taskapp',
]

Add a model for tasks in the taskapp/models.py

from django.db import models

class Task(models.Model):
    title = models.CharField(max_length=200)
    completed = models.BooleanField(default=False)
    created = models.DateTimeField(auto_now_add=True)

    def __str__(self):
        return self.title

Next, register the model intaskapp/admins.py

from django.contrib import admin
from .models import Task

admin.site.register(Task)

On default django has an admin panel that helps you manage your database models. To access it, you need to create a superuser account using the python manage.py createsuperuser command. fill in your username, password and email. The terminal doesn't show the password while you're typing,i know mine doesn't.

Run python manage.py makemigrations andpython manage.py migrate to migrate the task model. Close the terminal with Ctrl+c then Run python manage.py runserver to start the server.

Paste the url http://127.0.0.1:8000/ in your browser, you should see this django default page.

Screenshot (222).png

Open the admin url http://127.0.0.1:8000/admin in your browser and login with your superuser credentials.

Screenshot (223).png

Click add next to tasks and add as many tasks as you want.

Template for tasks

Create a templates folder in your taskapp directory and add a file task.htmlto it.

Screenshot (224).png

The task.html template will contain the list of tasks, and edit and a delete button. To add tasks, we will need a form that will contain fields we can import into our template. Create a forms.py file in your taskapp directory.

from django import forms
from django.forms import ModelForm
from .models import *

class TaskForm(forms.ModelForm):

    class Meta:
        model = Task
        fields ='__all__'

From the snippet above, i used import * to import all models, currently we have only one. The fields is set to all, you can exclude fields you don't what using exclude= fieldname. Models forms allow us to create forms for any model in the models.py

To display the tasks in the database models, django has a views.py file that acts as a controller...

 from django.shortcuts import render
from .models import *
from .forms import *

def home(request):
    tasks = Task.objects.all()
    form = TaskForm()
    context = {'tasks':tasks, 'form':form}

    return render(request, 'task.html', context)

home is the name of the views, i queried the task model for all objects, the called the form and passed the form into a context for it to be rendered on the html page.

Earlier in the article, we started the server the homepage was the default django template. Now, we're adding ours. Create a urls.py file in your taskapp directory and add a url path for the new homepage we wiil be using.

from django.contrib import admin
from django.urls import path, include
from .views import *

urlpatterns = [
    path('', home),

]

In your djangotodo project directory add a url for the taskapp to the urls.py.

from django.contrib import admin
from django.urls import path,include

urlpatterns = [

       path('admin/', admin.site.urls),
       path('',include('taskapp.urls')),

]

Django uses template tags wrapped with { to render the data in the html file. In your html file, we're going to loop through the context and list out the tasks.

<!DOCTYPE html>


<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <title>Tasks</title>
    <meta name="description" content="">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <link rel="stylesheet" href="">
</head>

<head>
    <link href="https://fonts.googleapis.com/css2?family=Hind+Siliguri:wght@300;400;500;600;700&display=swap" rel="stylesheet">
</head>

<body>
    <form method="POST" action="/">{% csrf_token %} {{form.title}}
        <input type="submit" name="Create task"> </form>

    <table>
        <thead>
            <tr>
                <th>Title</th>
                <th>Date</th>
                <th>Status</th>
                <th>Author</th>
                <th></th>
            </tr>
        </thead>
        {% for task in tasks %}
        <tbody>
            <tr class="table-row">
                <td>{{task}}</td>
                <td>{{task.created}}</td>
                <td>
                    <span class="status-icon review">
                    </span> {{task.completed|default:"Not complete"}}
                </td>
                <td>{{request.user}}</td>
            </tr>


        </tbody>
        {% endfor %}
    </table>
</body>

</html>

The above html code shows a form and a table, the form uses a POST method which is for sending data from the frontend to the backend. POST requests are used when we want to modify the database. The action indicates what page it redirects to once the request is done.

Your Home page should look like this

Screenshot (225).png

The tbody tag is wrapped with the for statement so that we can loop through the context and provide access to the fields in the Task model. Individual fields (created)in the model can be accessed by using a. . We can set default for some fields,if there isn't anything in it using |default:"Not complete" as seen in the task.completed. request.user displays your username of your currently logged in account.

We've done the create and read aspect,we're left with the update and delete.

Update task

for the update task, add a path url to the taskapp urls.py. The <str:pk> is to pass the object primary key or id to the url.

path('update-task/<str:pk>/', updateTask, name="updateTask"),

Views

def updateTask(request, pk):

    task = Task.objects.get(id=pk)
    form = TaskForm(instance=task)
    context = {'form':form}
    return render(request, 'edit-task.html',context)

edit-task.html

<body>
    <form method="POST" action="/">{% csrf_token %} {{form}}
        <input type="submit" name="Update task"> </form>
</body>

Delete task

urls.py path('delete-task//', deleteTask, name="deleteTask"),

views

def deleteTask(request, pk):
    task = Task.objects.get(id=pk)
    if request.method == 'POST':
        task.delete()
        return redirect('/')
    context={'task':task}
    return render(request, 'delete-task.html', context)

delete-task.html

<body>
    Are you sure you want to delete "{{task}}"?
    <form method="POST" action="">{% csrf_token %} {{form}}
        <input type="submit" name="Confirm"> </form>

</body>

The new task.html should look like this.

<!DOCTYPE html>

<body>
    <form method="POST" action="/">{% csrf_token %} {{form.title}}
        <input type="submit" name="Create task"> </form>

    <table>
        <thead>
            <tr>
                <th>Title</th>
                <th>Date</th>
                <th>Status</th>
                <th>Author</th>
                <th></th>
            </tr>
        </thead>
        {% for task in tasks %}
        <tbody>
            <tr class="table-row">
                <td>{{task}}</td>
                <td>{{task.created}}</td>
                <td>
                    <span class="status-icon review">
                    </span> {{task.completed|default:"Not complete"}}
                </td>
                <td>{{request.user}}</td>
                <td>
                    <a href="{% url 'updateTask' task.id %}">Edit</a>
                </td>
                <td>
                    <a href="{% url 'deleteTask' task.id %}">Delete</a>
                </td>
            </tr>


        </tbody>
        {% endfor %}
    </table>
</body>

</html>

I passed task.id into the url

Conclusion

We've been able to create a simple todo app with crud(Create,Read,Update) functionality. In my next article, i will be writing on how to style the forms used, create login, signup and a password page.