Welcome back! You've already made great progress by learning how to list and create todo items in your Flask
application. Now, we will enhance your ToDo app by adding the ability to edit existing todo items. This feature is essential in any task management app, as it allows users to update and refine their tasks as needed.
By the end of this lesson, you will be able to:
- Retrieve an existing todo item for editing.
- Update the todo item with new details and save the changes.
Let's dive in!
First, let's focus on the TodoService
class and the update
method. This method will allow us to modify the existing todo items.
Python1class TodoService: 2 3 # Constructor and other methods... 4 5 # Method to retrieve an item by its ID 6 def get_by_id(self, todo_id): 7 # Iterate through the list of todos to find the matching ID 8 for todo in self._todos: 9 # Return the matching todo item 10 if todo.todo_id == todo_id: 11 return todo 12 # Return None if no matching item is found 13 return None 14 15 # Method to update an item using its ID 16 def update(self, todo_id, title, description): 17 # Retrieve the todo item by its ID 18 todo = self.get_by_id(todo_id) 19 if todo: 20 # Update the title and description of the todo item 21 todo.title = title 22 todo.description = description 23 # Return True if the update was successful 24 return True 25 # Return False if the todo item was not found 26 return False
Here, we define two methods within the TodoService
class. The get_by_id
method searches for and returns a todo item by its unique identifier. If found, the item is returned; otherwise, None
is returned.
The update
method leverages this retrieval functionality and updates an existing todo item's title and description. If the item is found and updated, the method returns True
; otherwise, it returns False
.
Now, let's move on to the controller layer. We will handle routes for displaying the edit form and for submitting updates.
Here are the relevant routes in todo_controller.py
:
Python1from flask import Blueprint, render_template, request, redirect, url_for 2from services.todo_service import TodoService 3 4todo_service = TodoService() 5todo_controller = Blueprint('todo', __name__) 6 7# Routes for listing and adding items... 8 9# Route for rendering a page for a specific todo item 10@todo_controller.route('/edit/<int:todo_id>', methods=['GET']) 11def edit_todo(todo_id): 12 # Retrieve the todo item by its ID 13 todo_item = todo_service.get_by_id(todo_id) 14 if not todo_item: 15 # Redirect to list view if the todo item is not found 16 return redirect(url_for('todo.list_todos')) 17 # Render the edit template with the retrieved todo item 18 return render_template('todo_edit.html', todo=todo_item) 19 20# Route for updating the data of a specific todo item 21@todo_controller.route('/update/<int:todo_id>', methods=['POST']) 22def update(todo_id): 23 title = request.form.get('title') 24 description = request.form.get('description') 25 if title and description: 26 # Update the todo item with new title and description 27 todo_service.update(todo_id, title, description) 28 # Redirect to the list view after updating 29 return redirect(url_for('todo.list_todos'))
In this segment, we set up two new routes:
- Edit Route: The
edit_todo
route displays a form for editing a specific todo item. It uses theget_by_id
method to retrieve the item and renders thetodo_edit.html
template for editing. If the item isn't found, it redirects back to the list view. - Update Route: The
update
route handles form submission, extracts the new title and description from the request, updates the todo item using theupdate
method, and then redirects back to the main todo list. Although there are other HTTP methods more suited for update operations, such as PUT, we will use POST because HTML forms only natively support GET and POST methods.
Note that we use route parameters (e.g., <int:todo_id>
) to capture dynamic values directly from the URL. For example, <int:todo_id>
means todo_id
is an integer parameter. In a URL, it might look like /edit/1
where 1
is the todo_id
.
This allows us to pass the specific todo_id
to the route functions for precise item handling.
Before we create the editing form, we need to modify the part of our main page template that lists the items to include a way to select a specific todo item and redirect to its editing page.
Update the todo_list.html
template as follows:
HTML, XML1<ul> 2 {% for todo in todos %} 3 <li> 4 <strong>{{ todo.title }}</strong>: {{ todo.description }} 5 <!-- Link to the edit page for the specific todo item --> 6 <a href="{{ url_for('todo.edit_todo', todo_id=todo.todo_id) }}">Edit</a> 7 </li> 8 {% endfor %} 9</ul>
We upgrade our todo_list.html
template by adding an "Edit" link next to each todo item. For every todo item listed, the link redirects to the edit_todo
route, where users can edit the specific todo item. This link is generated using Flask's url_for
function, which dynamically creates the appropriate URL based on the todo_id
.
Next, let's create the HTML template with the form for editing a todo item.
Here is the todo_edit.html
template:
HTML, XML1<!DOCTYPE html> 2<html lang="en"> 3<head> 4 <meta charset="UTF-8"> 5 <meta name="viewport" content="width=device-width, initial-scale=1.0"> 6 <title>Edit Todo</title> 7</head> 8<body> 9 <h1>Edit Todo</h1> 10 11 <!-- Form for editing the selected todo --> 12 <form action="{{ url_for('todo.update', todo_id=todo.todo_id) }}" method="POST"> 13 <!-- Field for editing the title with current value pre-filled --> 14 <label for="title">Title:</label> 15 <input type="text" id="title" name="title" value="{{ todo.title }}" required><br><br> 16 <!-- Field for editing the description with current value pre-filled --> 17 <label for="description">Description:</label> 18 <input type="text" id="description" name="description" value="{{ todo.description }}" required><br><br> 19 <!-- Button to submit the form and update the todo item --> 20 <button type="submit">Update</button> 21 </form> 22 <!-- Link to go back to the main todo list --> 23 <a href="{{ url_for('todo.list_todos') }}">Back to List</a> 24</body> 25</html>
We then create the todo_edit.html
template, which displays a form for editing a specific todo item. The form is pre-filled with the current title and description of the todo item, allowing users to make changes. Upon submitting the form, the data is sent to the update
route using a POST request. Additionally, we provide a "Back to List" link to return to the main todo list without making any changes.
Let's walk through an end-to-end example to ensure everything works seamlessly:
-
Initiate Edit:
- From the list view, click the "Edit" link next to a todo item. This action will navigate to the
/edit/<int:todo_id>
route using the item's ID.
- From the list view, click the "Edit" link next to a todo item. This action will navigate to the
-
Edit Form:
- The form in
todo_edit.html
is displayed with the current details of the todo item pre-filled.
- The form in
-
Submit Changes:
- Modify the title and description as needed, and click the "Update" button. This submits the form to the
/update/<int:todo_id>
route using the item's ID.
- Modify the title and description as needed, and click the "Update" button. This submits the form to the
-
Update Processing:
- The
update
method inTodoService
is called to apply the changes. - Finally, the user is redirected back to the main todo list view, where the updated item is displayed.
- The
In this lesson, you learned how to enhance your ToDo app by enabling users to edit existing todo items. We covered:
- Updating the service layer to support modifications.
- Handling edit and update requests in the controller.
- Creating an edit form in the user interface.
Next, proceed to the practice exercises to apply these concepts and solidify your understanding. Great job, and keep up the excellent work in building your ToDo app!