Today, we’re diving into something really cool: let’s create a Todo list with Flask. It’s all about making a web app where managing tasks is super simple. You’ll learn how to make adding and deleting tasks smooth and straightforward; Flask is going to help us make our app super interactive and fun to use. So, are you in? Let’s start building something awesome together!
If you’re curious about building a Todo list with a different approach, check out our tutorial on Making a Todo List Using Tkinter for insights on creating a desktop application.
Table of Contents
- Getting Started
- Imports
- Creating the Flask App Instance
- Initializing the Todo List
- Defining Routes
- Running the Flask Application
- Full Code
- Example
- Wrapping Up
Getting Started
Before we dive in, install the Flask library via the terminal or your command prompt for the code to function properly:
$ pip install flask
Also, make sure to create ‘templates‘ and ‘static‘ directories in your project directory. The ‘templates‘ directory should contain the HTML files, while the ‘static‘ directory is for files like ‘style.css‘ and ‘app.js‘.
Imports
We begin by importing the Flask
class to create our application instance, which we can then customize. Next, we import the render_template
function, which enables us to render HTML templates. Lastly, we import request
to handle HTTP requests.
from flask import Flask, render_template, request
Creating the Flask App Instance
In this part, we will create an instance of the main Flask
class to represent our application, assigning it to the variable named app
.
app = Flask(__name__)
Initializing the Todo List
This line initializes a list in which we will store our tasks for the Todo list, needless to say, it is empty.
tasks = [] # Example tasks list
Defining Routes
Index Route
This route guides the user to the app’s main webpage upon visiting the generated URL. Within this route, we define the index()
function that is triggered when the user accesses the URL. This function calls the render_template
function, which renders ‘index.html‘ and passes the tasks list to it, so everything will be displayed (the index HTML and the tasks list).
@app.route('/')
def index():
return render_template('index.html', tasks=tasks)
Delete Task Route
When a POST request (identifying the task to be deleted) is sent to the delete URL, the delete_task()
function springs into action. It first tries to find the task in our list using the information from the JSON data, specifically with request.json.get('task')
. If the task is there, it’s removed with the remove()
method, and a confirmation message “Task deleted successfully” is returned, along with a 200 status code to signal success.
If the task isn’t in the list, though, the situation is handled gracefully. Instead of a success message, a “Task not found” message is sent back, accompanied by a 404 status code. This tells you that the task you wanted to delete couldn’t be found.
@app.route('/delete', methods=['POST'])
def delete_task():
task = request.json.get('task')
if task in tasks:
tasks.remove(task)
return 'Task deleted successfully', 200
else:
return 'Task not found', 404
Running the Flask Application
This block runs our app on debug mode while also ensuring that this script can only be run directly and not imported as a module.
if __name__ == "__main__":
app.run(debug=True)
Full Code
from flask import Flask, render_template, request
app = Flask(__name__)
tasks = [] # Example tasks list
@app.route('/')
def index():
return render_template('index.html', tasks=tasks)
@app.route('/delete', methods=['POST'])
def delete_task():
task = request.json.get('task')
if task in tasks:
tasks.remove(task)
return 'Task deleted successfully', 200
else:
return 'Task not found', 404
if __name__ == "__main__":
app.run(debug=True)
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Today's-To-Do List App - The Pycodes</title>
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@400;700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
</head>
<body>
<div class="container">
<h1>The Pycodes</h1>
<h1>Today's-To-Do List</h1>
<form id="task-form">
<input type="text" id="task-input" placeholder="What do you need to do?">
<button id="add-task-btn">+</button>
</form>
<ul id="task-list">
{% for task in tasks %}
<li>{{ task }} <button class="delete-btn" data-task="{{ task }}">Delete</button></li>
{% endfor %}
</ul>
</div>
<script src="{{ url_for('static', filename='app.js') }}"></script>
</body>
</html>
Document Type and Declaration:
These two lines specify the type of the document (HTML) and the language of the document (English).
<!DOCTYPE html>
<html lang="en">
Head Section:
We start with the metadata such as the character encoding of the document and viewport settings, and then we go to the title of the webpage, after that, we’re going to link the CSS link that imports Roboto font from Google fonts and the ‘style.css‘ from the ‘static‘ file directory with the HTML so it can use them to style the webpage using the url_for
function.
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Today's-To-Do List App - The Pycodes</title>
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@400;700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
</head>
Body Section:
This concerns the content of the container like the Heading and allowing users to input texts in input fields and adding placeholder text that gives hints to users, as well as a button that adds tasks, and a task list that displays the tasks with a delete button for each, while also making the webpage more interactive by using the url_for
function to link ‘app.js‘ which is a Javascript from the ‘static‘ directory to this HTML.
<body>
<div class="container">
<h1>The Pycodes</h1>
<h1>Today's-To-Do List</h1>
<form id="task-form">
<input type="text" id="task-input" placeholder="What do you need to do?">
<button id="add-task-btn">+</button>
</form>
<ul id="task-list">
{% for task in tasks %}
<li>{{ task }} <button class="delete-btn" data-task="{{ task }}">Delete</button></li>
{% endfor %}
</ul>
</div>
<script src="{{ url_for('static', filename='app.js') }}"></script>
</body>
</html>
style.css
/* style.css */
body {
font-family: 'Roboto', sans-serif;
margin: 0;
padding: 0;
background-color: #f8f9fa;
}
.container {
max-width: 500px;
margin: 50px auto;
padding: 30px;
background-color: #fff;
border-radius: 10px;
box-shadow: 0 0 20px rgba(0, 0, 0, 0.1);
text-align: center;
}
h1 {
font-size: 36px;
color: #333;
margin-bottom: 30px;
text-align: center;
}
form {
display: flex;
justify-content: center;
margin-bottom: 20px;
}
#task-input {
flex: 1;
padding: 10px;
border: 1px solid #ccc;
border-radius: 5px 0 0 5px;
font-size: 16px;
text-align: center;
}
#add-task-btn {
padding: 10px 20px;
border: none;
background-color: #28a745;
color: #fff;
border-radius: 0 5px 5px 0;
cursor: pointer;
font-size: 16px;
}
#add-task-btn:hover {
background-color: #218838;
}
ul {
list-style-type: none;
padding: 0;
}
li {
padding: 15px;
border-bottom: 1px solid #eee;
display: flex;
align-items: center;
justify-content: space-between;
}
.delete-btn {
background-color: #dc3545;
color: #fff;
border: none;
padding: 5px 10px;
border-radius: 3px;
cursor: pointer;
}
.delete-btn:hover {
background-color: #c82333;
}
This part defines the structure and the style of the content of our HTML template such as the container, the headings, the input fields, the background color, the font, the add task button, and the delete button.
app.js
document.addEventListener('DOMContentLoaded', function() {
const taskForm = document.getElementById('task-form');
const taskInput = document.getElementById('task-input');
const taskList = document.getElementById('task-list');
// Function to create a new list item for a task
function createTaskElement(taskText) {
// Create list item
const listItem = document.createElement('li');
listItem.textContent = taskText;
// Create delete button
const deleteButton = document.createElement('button');
deleteButton.textContent = 'Delete';
deleteButton.classList.add('delete-btn');
deleteButton.setAttribute('data-task', taskText);
// Add event listener to delete button
deleteButton.addEventListener('click', function() {
listItem.remove(); // Remove the task from the list
});
// Append delete button to list item
listItem.appendChild(deleteButton);
return listItem;
}
// Event listener for task form submission
taskForm.addEventListener('submit', function(event) {
event.preventDefault(); // Prevent default form submission behavior
const taskText = taskInput.value.trim(); // Get the task text and trim whitespace
if (taskText !== '') {
const listItem = createTaskElement(taskText); // Create list item for the task
taskList.appendChild(listItem); // Append list item to task list
taskInput.value = ''; // Clear the input field
}
});
// Event delegation for delete buttons (to handle dynamically created buttons)
taskList.addEventListener('click', function(event) {
if (event.target.classList.contains('delete-btn')) {
event.target.parentElement.remove(); // Remove the task list item when delete button is clicked
}
});
});
Event Listener:
This part ensures that this Javascript code runs inside this function only after the HTML is fully loaded.
document.addEventListener('DOMContentLoaded', function() {
Variable Initialization:
This one retrieves the relevant elements in the HTML document (task-form
, task-input
, task-list
) using their ID.
const taskForm = document.getElementById('task-form');
const taskInput = document.getElementById('task-input');
const taskList = document.getElementById('task-list');
Create Task Element Function:
This section takes the input and makes it into a new task item in the task list, then creates delete buttons and associates them with removal to make handling them later easy, and finally to each input (task) added to the list it adds a delete button next to it.
// Function to create a new list item for a task
function createTaskElement(taskText) {
// Create list item
const listItem = document.createElement('li');
listItem.textContent = taskText;
// Create delete button
const deleteButton = document.createElement('button');
deleteButton.textContent = 'Delete';
deleteButton.classList.add('delete-btn');
deleteButton.setAttribute('data-task', taskText);
// Add event listener to delete button
deleteButton.addEventListener('click', function() {
listItem.remove(); // Remove the task from the list
});
// Append delete button to list item
listItem.appendChild(deleteButton);
return listItem;
}
Event Listener for Task Form Submission:
This part listens for when the user submits the input to add a new task. What I mean by this is this part is activated when the user clicks the add task button, and once activated it ensures that the page doesn’t reload or refresh.
// Event listener for task form submission
taskForm.addEventListener('submit', function(event) {
Handling Task Form Submission:
This one verifies if the user has entered the new task or not, if so then it retrieves the input task and trims it from any leading or trailing whitespace then creates a new task item, adds it to the list, and then clears the input field.
event.preventDefault(); // Prevent default form submission behavior
const taskText = taskInput.value.trim(); // Get the task text and trim whitespace
if (taskText !== '') {
const listItem = createTaskElement(taskText); // Create list item for the task
taskList.appendChild(listItem); // Append list item to task list
taskInput.value = ''; // Clear the input field
}
});
Event Delegation for the Delete Buttons:
when the user clicks the delete button the listener informs the next part that a delete button in the tasks list has been clicked. After being informed by the listener this part deletes the task which the user chose to remove by clicking the delete button.
// Event delegation for delete buttons (to handle dynamically created buttons)
taskList.addEventListener('click', function(event) {
if (event.target.classList.contains('delete-btn')) {
event.target.parentElement.remove(); // Remove the task list item when delete button is clicked
}
});
});
Example
Wrapping Up
We’ve explored the fun and functionality of building a dynamic Todo list with Flask. By adding and deleting tasks, our web app becomes a helpful and engaging tool. It’s been awesome walking you through each step of this journey.
Happy Coding!