Django Sample
Django Sample
First Edition
All rights reserved. No part of this book may be reproduced, stored in a retrieval system, or
transmitted in form or by any means, without the prior written permission of the author, except
in the case of brief quotations embedded in critical articles or reviews.
Every effort has been made in the preparation of this book to ensure the accuracy of the
information presented. However, the information contained in this book is sold without
warranty, either express or implied.
Table of Contents
HOW TO FOLLOW?................................................................................................................................ 2
We also highlight important code for better understanding. Every time we reference a file, we clearly
mention its directory path. If you see a file path mentioned but cannot find the file, that means it’s a new
file — you should create it and proceed accordingly.
Please follow the steps carefully and pay close attention to file names and paths to avoid confusion.
§ Newsletter
§ Comments
§ Rich Text Editor
§ Clean, professional design
We’ll provide the GitHub repository link for each project in the final chapter.
Install Node.js:
👉 https://nodejs.org/en/download
Install PostgreSQL and pgAdmin:
Follow these guides to install PostgreSQL and connect it using pgAdmin:
Þ https://www.w3schools.com/postgresql/postgresql_install.php
Þ https://www.w3schools.com/postgresql/postgresql_pgadmin4.php
During installation, make sure to remember the password for the postgres user. Forgetting it can lead to
unnecessary hassle.
Install Git and Setup GitHub:
Þ https://youtu.be/p0Js7IF17yI?si=2sPdYb98x2kqAb7Q (macOS)
Þ https://youtu.be/AdzKzlp66sQ?si=JVuLYN6fLuZs-kin (Windows)
❓ If you get stuck, you can always search on YouTube — there are plenty of helpful tutorials on installing
these tools. And remember, ChatGPT is always there to help you out!
Shell(macOS)
macbook@MacBooks-MacBook-Pro ~ % python3 --version
Python 3.13.3
Shell(Windows)
PS C:\Users\Gonzalo> python --version
Python 3.13.3
❗Whenever you see the command python3, Windows users should use python instead. macOS users should
continue using python3. I won’t show separate shells just for this difference. Thanks!
Shell
# node --version
v.22.15.0
Shell
# npm --version
10.9.2
Shell
# psql --version
psql (PostgreSQL) 17.4
Shell
# git --version
git version 2.49.0
Throughout this course, I’ll use a shell interface — meaning the commands shown will work for both macOS
and Windows. In cases where the commands differ between operating systems, I’ll clearly mention which
shell or platform they apply to, so you won’t face any confusion.
1. Python
2. Python-Debugger
3. Django
4. Django-Snippets
5. Prettier-Code-formatter
6. Tailwind-CSS-IntelliSense
7. Auto-Close-Tag
8. Auto-Rename-Tag
Once VS Code is open, your editor should look like this. Click the square icon with four squares (for
Extensions) and the arrow icon or terminal/shell tab to open the Extensions panel and the Terminal/Shell,
respectively.
This will create a new folder called todo_project, move into it, and open it in VS Code.
If you're inside the todo_project folder, you're all set to move forward. If not, please revisit this chapter and
follow the steps carefully from the beginning.
Let’s continue! 🚀
Create and activate virtual environment:
Shell(macOS)
# python3 -m venv env
# source env/bin/activate
Shell(Windows)
# python -m venv env
# env/Scripts/activate
You can name your virtual environment anything — it doesn’t have to be env. After activation, you’ll see the
environment name (like (env)) appear at the beginning of your terminal line.
📌 Remember: Every time you open this project (or any Django project), your first task is to activate the
virtual environment. You know how to do that now — right?
Shell(macOS)
# source env/bin/activate
Shell(Windows)
# env/Scripts/activate
How to deactivate?
Shell
# deactivate
Before moving ahead, double-check that your virtual environment is actually activated. You should see the
environment name (like (env)) at the beginning of your terminal line.
If you don’t see it, activate it again using the appropriate command.
Shell
# python3 -m pip install Django
Windows user? Remember, I have told you earlier that you should use python instead python3—right?
Shell
# django-admin --version
5.2.1
Shell
# django-admin startproject config .
📌 Remember: You can name your project anything — it doesn’t have to be config. Don’t forget the dot (.)
and the space after config. Missing the dot will create an extra nested folder, which can be confusing later.
Shell
# python3 manage.py runserver
Watching for file changes with StatReloader
Performing system checks...
System check identified no issues (0 silenced).
You have 18 unapplied migration(s). Your project may not work properly until you apply the migrations
for app(s): admin, auth, contenttypes, sessions.
Run 'python manage.py migrate' to apply them.
May 15, 2025 - 15:32:21
Django version 5.2.1, using settings 'config.settings'
Starting development server at http://127.0.0.1:8000/
Quit the server with CONTROL-C.
WARNING: This is a development server. Do not use it in a production setting. Use a production WSGI
or ASGI server instead.
For more information on production servers see:
https://docs.djangoproject.com/en/5.2/howto/deployment/
Hold Cmd (on macOS) or Ctrl (on Windows) and click the link below (In shell) to open it in Chrome (or your
default browser, I strongly prefer to use chrome): http://127.0.0.1:8000/
You should see Django’s welcome page if everything is working correctly.
Django creates a default SQLite database (db.sqlite3). Since we’ll use PostgreSQL, let’s delete it.
You can manually delete it or hit this command:
Shell(macOS)
# rm db.sqlite3
Shell(Windows)
# del db.sqlite3
Shell
# npm install tailwindcss @tailwindcss/cli
After running the command, you’ll see that two files — package.json and package-lock.json — and a node_modules
folder have been added to the root (todo_project) folder. This indicates that Tailwind was successfully
initialized.
Now, inside the root folder, create a static folder. Within it, create a src folder → then a css folder inside src →
and finally an input.css file inside the css folder.
todo_projects/static/src/css/input.css
@import "tailwindcss";
todo_projects/package.json
{
"dependencies": {
"@tailwindcss/cli": "^4.1.7",
"tailwindcss": "^4.1.7"
},
"scripts": {
"dev": "npx @tailwindcss/cli -i ./static/src/css/input.css -o ./static/src/css/output.css --
watch"
}
}
Shell
# npm run dev
Now you’ll see that inside css folder a file created namely output.css file. If not you can manually create it
and run the command again.
Now let’s check is tailwindcss working or not. Crate a folder namely templates inside root (todo_projects)
folder. Inside this folder create base.html file.
Now create a Django app namely todo, you can choose anything instead of todo:
Shell
# django-admin startapp todo
After running this command, now you’ll notice a folder namely todo created within the root(todo_projects)
folder with some files. Your project structure will now look like this:
Whenever you create any app in Django you have to mention this settings.py file project (config) folder.
Let’s do this:
todo_project/config/settings.py
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
# Django app
'todo',
Now go to views.py file of todo app folder and add these lines:
todo_project/todo/views.py
from django.shortcuts import render
Now to access this view function, we have to mention this function in urls.py file of config folder. Let’s do
this.
todo_project/config/urls.py
from django.contrib import admin
from django.urls import path
from todo.views import welcome_page
urlpatterns = [
path('admin/', admin.site.urls),
path('', welcome_page), when someone visit the root url, show them welcome_page view
]
Now let’s check does this bring our base.html file in our webpage. Run this command:
What happened! Oh! We created a templates folder but didn’t mention this or configure this in our settings.py
file! Let’s do this. Open settings.py file:
todo_project/config/settings.py
INSTALLED_APPS = [
TEMPLATES = [
{
'BACKEND': 'django.template.backends.django.DjangoTemplates',
'DIRS': [BASE_DIR/ 'templates'],
'APP_DIRS': True,
'OPTIONS': {
'context_processors': [
'django.template.context_processors.request',
'django.contrib.auth.context_processors.auth',
'django.contrib.messages.context_processors.messages',
],
},
},
]
But not exactly what we expected! We expected that tailwind will also be implemented! But it’s not.
Let’s figure it out!
Two things we maybe forgot to do! Let’s check the base.html. Yeap! We didn’t mention the output.css file in
our <head> section. Let’s mention this.
todo_project/templates/base.html
{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" href="{% static 'src/css/output.css' %}" />
<title>TODO LIST - Home</title>
</head>
<body>
<h1 class="text-orange-500">Hello world!</h1>
</body>
</html>
Now refresh the page again! It’s not working! We expected to see an orange color normal “Hello world!”
message! What happened this time?
Oh! We have created a static folder inside root (todo_project) but didn’t configure it in settings.py file.
Let’s configure it: Add the highlighted line.
todo_project/config/settings.py
STATIC_URL = 'static/'
STATICFILES_DIRS = [BASE_DIR/ "static"]
Now add another line to the base.html file with tailwindcss red color:
todo_project/templates/base.html
{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" href="{% static 'src/css/output.css' %}" />
<title>TODO LIST - Home</title>
Go to the page. Nothing happened! Refresh the page! It is working! But how many time we have to refresh?
Every time we made a tiny change we have to refresh! It’s boring right? Let’s solve it.
Even for this small project, we’ll install a package that automatically reloads the browser whenever we
make changes to our HTML files. This way, you won’t need to manually press the refresh button — changes
will appear instantly. Let’s install the django-browser-reload package.
Shell
# pip install django-browser-reload
Now let’s configure settings.py. I’ve highlighted the lines where changes or additions have been made.
todo_projects/config/settings.py
# Application definition
INSTALLED_APPS = [
'django.contrib.admin',
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
'django.contrib.messages',
'django.contrib.staticfiles',
# Django app
'todo',
# Installed third party apps
'django_browser_reload'
]
MIDDLEWARE = [
'django.middleware.security.SecurityMiddleware',
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
"django_browser_reload.middleware.BrowserReloadMiddleware", #related to
django_browser_reload
]
todo_project/config/urls.py
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('', welcome_page),
path("__reload__/", include("django_browser_reload.urls")),
]
It’s done! Now, any changes you make in the HTML file will automatically reload the browser — no need to
refresh manually. This feature is often referred to as HOT RELOAD.
Currently, the way we’ve defined the URL for welcome_page isn’t ideal. While it's fine for a small app, in larger
applications with multiple apps — like one for user management, another for blogging, etc. — it’s better to
define the URLs related to a specific app within that app. This keeps the business logic organized and makes
the project easier to maintain.
Let’s redefine the URL for the welcome_page in a more structured and maintainable way. First, we need to
include the todo app in the project’s urls.py file. Let’s do it.
todo_project/config/urls.py
from django.contrib import admin
from django.urls import path, include
urlpatterns = [
path('admin/', admin.site.urls),
path('', include('todo.urls')),
path("__reload__/", include("django_browser_reload.urls")),
]
The highlighted line, path('', include('todo.urls')), means: “Look for an app named todo in the project, and if
it has a urls.py file, include its URL patterns here.”
So, whenever someone visits a URL, Django will first check this urlpatterns list. If it matches the empty string
'', it will then look into the todo/urls.py file for further matching. This helps keep each app's routing organized
and allows Django to delegate URL handling to the specific app responsible for that logic.
Now, we don’t have a urls.py file inside the todo app, so let’s create one and add the following code:
todo_project/todo/urls.py
from django.urls import path
from . import views
urlpatterns = [
path('', views.welcome_page),
]
In VS Code, look at the top-right corner of the terminal panel — you’ll see a plus (+) icon. Click it to
open a second terminal. Now you can switch between them by simply clicking on each tab.
📌 Remember: Always activate your virtual environment in each terminal before running any commands.
Earlier, we deleted the default db.sqlite3, so now we need to create a new PostgreSQL database.
1. Open pgAdmin.
2. On the top-left, expand Servers → then expand the PostgreSQL server.
3. Right-click on Databases → click Create → then Database.
4. A popup window will appear.
Þ Set a name for the database (e.g., todo_db).
5. On the bottom-right of the popup, click Save.
Done! Your PostgreSQL database is now created and ready to be connected to your Django project. Like
this way:
Now that the database is created, let’s connect it with our Django project. To do that, we need to install a
package called psycopg2, which allows Django to communicate with PostgreSQL.
Shell
# pip install psycopg2
todo_project/config/settings.py
DATABASES = {
'default': {
'ENGINE': 'django.db.backends.postgresql',
'NAME': 'todo_db', # name of the database you created
'USER': 'postgres', # your PostgreSQL username
'PASSWORD': '87654321', # your PostgreSQL password
'HOST': 'localhost',
'PORT': '5432',
}
}
In this section, we’ll build a basic CRUD (Create, Read, Update, Delete) application. CRUD represents the
core functionalities of most web apps.
To begin, we’ll redesign our homepage by updating the base.html template to support these operations. This
will serve as the starting point for adding tasks and managing them through a user-friendly interface.
todo_project/templates/base.html
{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" href="{% static 'src/css/output.css' %}" />
<!-- Font Awesome (Optional) -->
<link
rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css"
/>
<title>TODO LIST - Home</title>
</head>
<body class="bg-gray-100 text-gray-900">
<div class="max-w-6xl mx-auto px-4 py-8">
<h3 class="text-3xl font-bold text-center mb-8">Django ToDo App</h3>
After updating, let’s check the browser now. BOOM! Our design is ready to work!
Now let’s define our data structure by writing a few lines in the models.py file.
The models.py file is where we define the database schema for our Django app. It contains classes that
represent tables in the database. Each class is a model, and the attributes of the class represent fields/columns
in the table.
todo_project/todo/models.py
from django.db import models
Explanation:
class Task(models.Model):
Þ class Task: Defines a new model (i.e., database table) named Task.
Þ models.Model: Inherits from Django’s base model, giving Task all the functionality of a Django model.
task_name = models.CharField(max_length=250)
Þ task_name: This is a field (column) in the database.
Þ CharField: A text field for short strings.
Þ max_length=250: Limits the maximum length of the task name to 250 characters.
is_completed = models.BooleanField(default=False)
Þ is_completed: A field to track whether the task is done or not.
created_at = models.DateTimeField(auto_now_add=True)
Þ created_at: A field to automatically record when the task is created.
Þ auto_now_add=True: Automatically sets the field to the current time when the object is first created.
updated_at = models.DateTimeField(auto_now=True)
Þ updated_at: A field to automatically record when the task is last updated.
Þ auto_now=True: Updates this field to the current time every time the object is saved.
This is how models work in Django. The next step is to apply these changes to the database. To do that, we
need to run two important commands:
1. makemigrations – This generates a migration file based on the changes made to the models.py file.
Now let’s define our logic or view in views.py file. let’s do it:
todo_project/todo/views.py
from django.shortcuts import render, redirect
from .models import Task
def create_task(request):
task = request.POST['task']
Task.objects.create(task_name=task)
return redirect('welcome_page')
Explanation:
from .models import Task
Þ This line imports the Task model from the current app’s models.py file.
def addTask(request):
Þ This defines a function named addTask that takes in the HTTP request object as a parameter.
Þ It's meant to handle a form submission (typically via POST) for adding a new task.
task = request.POST['task']
Þ This line extracts the value submitted in the form under the key "task".
Þ request.POST is a dictionary-like object containing all form data sent via a POST request.
Þ For example, if your HTML form had an input like <input name="task">, this line captures that
value. Check base.html, you’ll find this.
Task.objects.create(task_name=task)
Þ This line creates a new Task object and saves it to the database.
Þ Task.objects.create(...) = create a new row in the Task table with the given data.
Þ task_name=task means: the model’s task field is being set to the value retrieved from the form.
return redirect('welcome_page')
Þ After saving the task, this line redirects the user to the 'welcome_page' URL (usually defined in
urls.py with name='welcome_page'). We’ll define it in the very next step.
todo_project/todo/urls.py
from django.urls import path
from . import views
urlpatterns = [
path('', views.welcome_page),
path('create_task/', views.create_task, name='welcome_page'),
]
Explanation:
path('create_task/', views.create_task, name='create_task'),
So this line tells Django: “When someone visits /create_task/, call the create_task function from
views.py, and let us refer to this URL pattern by the name 'create_task'.”
Now, we have to configure these changes to our ‘base.html’ file. so that our changes work accordingly.
todo_project/templates/base.html
‘’’
<form
action="{% url 'create_task' %}"
method="POST"
class="bg-white p-4 shadow rounded"
>
{% csrf_token %}
<div class="flex gap-2">
<input
type="text"
name="task"
class="flex-1 px-4 py-2 border border-gray-300 rounded focus:outline-none
focus:ring-2 focus:ring-blue-400"
placeholder="Add a task"
/>
<button
type="submit"
class="bg-blue-500 text-white px-5 py-2 rounded hover:bg-blue-600"
>
<i class="fa fa-plus"></i> Add
Now, when you submit the form, the data goes to the server since we're using the POST method.
Explanation:
action="{% url 'create_task' %}"
Þ action: This is an HTML attribute of the <form> tag. It tells the browser where to send the form data
when the form is submitted.
Þ "{% ... %}": This is a Django template tag. It allows dynamic content to be inserted in templates.
Þ url: A Django template tag that reverses a URL pattern name into the actual URL. So you don’t hard-
code URLs.
Þ 'create_task': This is the name of a URL pattern that you defined in your urls.py. Django will
look for a path with name='create_task' and insert its URL here.
method="POST"
Þ method: Another HTML attribute of the <form> tag. It specifies how the form data should be sent.
Þ "POST": A method type. It means the data will be sent securely in the body of the HTTP request, not
in the URL (as with GET).
{% csrf_token %}
Þ csrf_token: Stands for “Cross-Site Request Forgery token.” Django generates a hidden token that
protects your form from malicious submissions from other sites.
We’re almost done! But there’s one important thing left — we should be able to see the list of tasks we’ve
added on our Home page. For that we have to configure our welcome_page view. Let’s implement that
now.
todo_project/todo/views.py
‘’’
def welcome_page(request):
task_list = Task.objects.filter(is_completed=False).order_by('-updated_at')
context = {
'task_list': task_list,
}
return render(request, "base.html", context)
‘’’
Explanation:
task_list = Task.objects.filter(is_completed=False).order_by('-updated_at')
Þ task_list: A variable that will store a list of tasks we want to display.
Þ Task: This is the model we created to represent a task in the database.
Þ .objects: This gives us access to query the database (Django ORM).
Þ .filter(is_completed=False): This filters the tasks to get only those that are not completed
(unchecked tasks).
Þ .order_by('-updated_at'): This sorts the tasks by their last updated time, newest first (the - sign
means descending order).
context = { 'task_list': task_list,}
Þ context – A dictionary that passes data to the template (HTML file).
Þ 'task_list' – The key used in the template to access the data.
Þ task_list – The variable we created above, containing filtered and sorted tasks.
return render(request, "base.html", context)
Þ context – The data we’re sending to that template (so it can show the tasks).
Note:
todo_project/templates/base.html
‘’’
<!-- My Day -->
<div>
<h4 class="text-xl font-semibold mb-4">My Day</h4>
<div class="h-[450px] overflow-y-scroll space-y-2 pr-2">
{% for task in task_list %}
<div
class="bg-white shadow rounded p-4 flex justify-between items-center"
>
<span>{{ task.task_name }}</span>
<div class="space-x-2">
<a
href="#"
class="bg-green-500 text-white px-3 py-1 rounded hover:bg-green-600 text-sm"
>
<i class="fa fa-check"></i> Done
</a>
<a
href="#S"
class="bg-red-500 text-white px-3 py-1 rounded hover:bg-red-600 text-sm"
>
<i class="fa fa-trash"></i>
</a>
<a
href=""
class="bg-blue-500 text-white px-3 py-1 rounded hover:bg-blue-600 text-sm"
>
<i class="fa fa-pencil"></i>
</a>
Explanation:
{% for task in task_list %}
Þ {% ... %} – This is a template tag in Django. It tells the template to perform logic (not just display
text).
Þ for – A loop keyword, just like in Python. It means we want to go through a list, one item at a time.
Þ task – A temporary variable name we choose. In each loop, this will represent one task from the
list.
Þ in – A keyword that connects the variable (task) to the list (task_list) we are looping over.
Þ task_list – This is the name of the variable we passed from the view using cotext (a list of tasks not
completed).
{{ task.task_name }}
Þ {{ ... }} – This is a template expression that tells Django to display the value inside.
Þ task – This refers to the current task from the loop.
Þ task_name – This is the field from the model. It shows the name of the task.
{% endfor %}
Þ endfor – Tells Django that the loop is finished.
This block of code loops through all the tasks in task_list and displays each task’s name.
Great job! You've just learned one of the core concepts of Django — the MVT (Model-View-Template)
architecture.
Djnago Unchained for Beginners
34
Let’s understand what is connected with what:
Sometimes it can be confusing to understand how everything is connected in a Django project. But if you
carefully study the diagram, you'll gain a clear and confident understanding of how each part interacts with
the others.
What is MVT?
Django follows the MVT architectural pattern, which is very similar to MVC (Model-View-Controller),
but Django does a bit of the controller work internally for you.
Þ Model: Handles the data (database).
In a to-do application, we create tasks that need to be done. Once we complete a task, we check the box to
mark it as done — right? But sometimes, our boss might say, "No, this task isn’t finished yet. Please complete
it properly." In that case, we need to uncheck the task again. So, let’s implement that feature now!
todo_project/todo/views.py
from django.shortcuts import render, redirect, get_object_or_404
,,,
def task_checked(request, id):
task = get_object_or_404(Task, id=id)
task.is_completed = True
task.save()
return redirect('welcome_page')
,,,
Explanation:
def task_checked(request, id):
Þ id: A URL parameter that contains the ID of the task to be marked as completed.
task = get_object_or_404(Task, id=id)
Þ get_object_or_404(...): A Django shortcut function that:
1. Tries to retrieve an object from the database (Task model in this case).
2. If the object does not exist, it automatically returns an HTTP 404 (Not Found) response.
Þ id=id: Look for a task in the database where the task’s id (in the database) is the same as the id we
todo_project/todo/urls.py
from django.urls import path
from . import views
urlpatterns = [
path('', views.welcome_page, name='welcome_page'),
path('create_task/', views.create_task, name='create_task'),
path('task_checked/<int:id>/', views.task_checked, name='task_checked'),
]
Explanation:
path('task_checked/<int:id>/', views.task_checked, name='task_checked'),
Þ 'task_checked/<int:id>/':
§ task_checked/: The URL starts with this static path.
Now, mark a few of your tasks as completed and check the database to see the updates.
Yes! It’s working. The tasks we marked as completed are now showing with their boolean value set to True.
Let’s check this in the browser.
todo_project/templates/base.html
‘’’
<!-- Completed Tasks -->
<div>
<h5 class="text-xl font-semibold mb-4">Completed Tasks</h5>
{% for task in completed_task_list %}
<div class="space-y-2">
<div
class="bg-white shadow rounded p-4 flex justify-between items-center"
>
<span>{{task.task_name}}</span>
<a
href="#"
class="bg-red-500 text-white px-3 py-1 rounded hover:bg-red-600 text-sm"
>
<i class="fa fa-times"></i> Undone
</a>
</div>
</div>
{% endfor %}
</div>
</div>
‘’’
Great job! But sometimes, our teacher or friends say our completed task isn’t really done. In that case, we
need to uncheck it. Let’s see how—it’s simple and almost the same!
Let’s define view for that:
todo_project/todo/views.py
def task_unchecked(request, id):
task = get_object_or_404(Task, id=id)
task.is_completed = False
task.save()
return redirect('welcome_page')
todo_project/todo/urls.py
from django.urls import path
from . import views
urlpatterns = [
path('', views.welcome_page, name='welcome_page'),
path('create_task/', views.create_task, name='create_task'),
path('task_checked/<int:id>/', views.task_checked, name='task_checked'),
path('task_unchecked/<int:id>/', views.task_unchecked, name='task_unchecked'),
]
todo_project/templates/base.html
‘’’
<span>{{task.task_name}}</span>
<a
href="{% url 'task_unchecked' task.id %}"
class="bg-red-500 text-white px-3 py-1 rounded hover:bg-red-600 text-sm"
>
<i class="fa fa-times"></i> Undone
</a>
‘’’
todo_project/todo/views.py
def update_task(request, id):
get_task = get_object_or_404(Task, id=id)
if request.method == ‘POST’:
updated_task = request.POST[‘task’]
get_task.task_name = updated_task
get_task.save()
return redirect(‘welcome_page’)
else:
context = {
‘get_task’: get_task,
}
return render(request, ‘update_task.html’, context)
Explanation:
This view handles updating a task. It first fetches the task by its ID. If the request is a POST (form
submitted), it updates the task name with the new value and saves it, then redirects to the welcome page. If
it's a GET request, it renders a form with the current task data for editing.
todo_project/todo/urls.py
from django.urls import path
from . import views
urlpatterns = [
path('', views.welcome_page, name='welcome_page'),
path('create_task/', views.create_task, name='create_task'),
path('task_checked/<int:id>/', views.task_checked, name='task_checked'),
path('task_unchecked/<int:id>/', views.task_unchecked, name='task_unchecked'),
path('update_task/<int:id>/', views.update_task, name='update_task'),
]
todo_project/templates/base.html
‘’’
<a
href="{% url 'update_task' task.id %}"
class="bg-blue-500 text-white px-3 py-1 rounded hover:bg-blue-600 text-sm">
<i class="fa fa-pencil"></i>
</a>
‘’’
todo_project/templates/update_task.html
{% load static %}
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<link
rel="stylesheet"
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/4.7.0/css/font-awesome.min.css"
/>
</head>
<body class="bg-gray-100 min-h-screen flex items-center justify-center">
<div class="w-full max-w-md bg-white p-6 rounded shadow">
<h3 class="text-2xl font-bold text-center mb-6">
Django TODO App - Update Task
</h3>
<form
action="{% url 'update_task' get_task.id %}"
method="POST"
class="space-y-4">
{% csrf_token %}
<div>
<div class="flex gap-2">
<input
type="text"
name="task"
class="w-full border border-gray-300 rounded px-4 py-2 focus:outline-none
focus:ring-2 focus:ring-blue-400"
placeholder="Update your task"
value="{{get_task.task_name}}"
/>
<button
type="submit"
class="w-40 bg-blue-600 text-white px-4 py-2 rounded hover:bg-blue-700 transition"
>
<i class="fa fa-plus mr-1"></i> Update
</button>
</div>
</div>
</form>
</div>
</body>
</html>
If we now click the edit button, it will show the update_task.html file:
Yeah! It’s updated. Sometimes we no longer need a task, so we should delete it. Let’s implement this feature
too—it’s very simple, and you might already know how to do it!
todo_project/todo/views.py
def delete_task(request, id):
task = get_object_or_404(Task, id=id)
task.delete() # This delete the task from the database
return redirect('welcome_page')
todo_project/todo/urls.py
from django.urls import path
from . import views
urlpatterns = [
path('', views.welcome_page, name='welcome_page'),
path('create_task/', views.create_task, name='create_task'),
path('task_checked/<int:id>/', views.task_checked, name='task_checked'),
path('task_unchecked/<int:id>/', views.task_unchecked, name='task_unchecked'),
path('update_task/<int:id>/', views.update_task, name='update_task'),
path('delete_task/<int:id>/', views.delete_task, name='delete_task'),
]
todo_project/templates/base.html
,,,
<a href="{% url 'delete_task' task.id %}"
class="bg-red-500 text-white px-3 py-1 rounded hover:bg-red-600 text-sm">
<i class="fa fa-trash"></i>
</a>
,,,
Now click the delete icon button and check the database. Yeap, First task has been deleted!
We’ve successfully finished our todo project! Now, let’s move on and start our blog project!
Djnago Unchained for Beginners