Welcome back! In the previous lessons, we set up SQLAlchemy, mapped our Todo
model to a database table, and performed CRUD operations. This lesson focuses on enhancing our ToDo application by adding the ability to sort todos.
Sorting is an essential feature in any web application, as it improves user experience by making data easier to navigate and find. Today, we will learn how to implement this functionality step-by-step.
Before diving deeper, let's briefly remind ourselves of what sorting is.
Sorting arranges data in a specific order. Common types of sorting include:
- Alphabetical: Orders data from A-Z (ascending) or Z-A (descending).
- Numerical: Orders numbers in an ascending (0-9) or descending (9-0) sequence.
- Alphanumeric: Sorts data containing both letters and numbers, typically placing numbers before letters (e.g., '1 Task', '2 Task', 'Task 1', 'Task 2', 'Task 10').
In this lesson, we will implement alphanumeric sorting by the todo titles to help users find items more quickly.
Let’s start by updating the get_all
method in app/services/todo_service.py
to support sorting.
Here’s the updated method:
Python1from models.todo import Todo, db 2 3class TodoService: 4 @staticmethod 5 def get_all(sort_by=None): 6 # Initialize a query on the Todo model 7 query = Todo.query 8 # Apply sorting if sort_by is 'title' 9 if sort_by == 'title': 10 query = query.order_by(Todo.title) 11 # Execute the query and return all matching todos 12 return query.all()
Let's break this down:
- Initial Query:
- We start by initializing a query on the
Todo
model:query = Todo.query
. This sets up a base query to build on.
- We start by initializing a query on the
- Sorting:
- If
sort_by
is set to 'title', we sort the results:query = query.order_by(Todo.title)
. This arranges the todos alphanumerically by their titles. Ifsort_by
is anything other than 'title', all items will be fetched without sorting.
- If
- Fetching Results:
- Finally,
return query.all()
executes the query and returns all matching todos.
- Finally,
Now, we’ll modify app/controllers/todo_controller.py
to capture the sort parameter from the URL and pass it to our updated service method.
Here’s the updated controller:
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@todo_controller.route('/', methods=['GET']) 8def list_todos(): 9 # Capture sort parameter from the URL 10 sort_by = request.args.get('sort_by') 11 # Fetch sorted todos 12 todos = todo_service.get_all(sort_by=sort_by) 13 # Render the todo list template with the todos 14 return render_template('todo_list.html', todos=todos)
Let's break this down:
-
Capturing Parameter:
- We capture the
sort_by
parameter from the URL query string usingsort_by = request.args.get('sort_by')
. This allows us to dynamically read this value whenever a GET request is made to the root route (/
).
- We capture the
-
Fetching Sorted Data:
- We call
todo_service.get_all(sort_by=sort_by)
with the captured parameter. This fetches the todos based on the sort criteria specified by the user. If thesort_by
parameter isNone
, the method will return all todos.
- We call
-
Rendering the Template:
- We render the
todo_list.html
template with the fetched todos usingreturn render_template('todo_list.html', todos=todos)
. This passes the sorted list of todos to the template for display.
- We render the
The final step is to modify app/templates/todo_list.html
to add sort options in the form, enabling users to input their preferences.
Here’s the updated 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>Todo List</title> 7</head> 8<body> 9 <h1>Todo List</h1> 10 11 <!-- Form for sorting todos --> 12 <form action="{{ url_for('todo.list_todos') }}" method="GET"> 13 <div class="form-group"> 14 <label for="sort_by">Sort by:</label> 15 <select id="sort_by" name="sort_by"> 16 <option value="" selected>None</option> 17 <option value="title">Title</option> 18 </select> 19 </div> 20 <button type="submit">Apply</button> 21 </form> 22 23 <!-- List all todos... --> 24 25 <!-- Form for adding a new todo... --> 26 27</body> 28</html>
Key Additions:
- Form Setup: A form that submits GET requests to the
list_todos
route. - Sort By Dropdown: A dropdown menu to select sorting by title, with "None" as the default option.
- Submit Button: A button to apply the selected sort option.
If no sorting option is selected, the submit button will send None
to the sort_by
parameter, resulting in all todos being returned. These enhancements enable users to specify their sorting preferences, while still allowing for all items to be displayed if no specific options are chosen. When the form is submitted, the selected options are sent as query parameters to the list_todos
route, which then displays the sorted list of todos.
Now that we have implemented sorting, it’s essential to test the functionality.
-
Sorting:
- Use the sort dropdown to select "Title" and submit the form.
- The URL for sorting by title would be:
/todos?sort_by=title
- The items should be sorted alphanumerically by title.
-
Returning All Todos:
- Submit the form without selecting a sort option.
- The URL for returning all todos would be:
/
- All todos should be displayed without any sorting applied.
In this lesson, we enhanced our ToDo application by implementing sorting functionality. We:
- Updated the service layer to support dynamic sorting.
- Modified the controller to capture user input for sorting.
- Enhanced the template to allow users to input their sorting preferences.
- Provided strategies to test the new functionality.
These improvements make our application more robust and user-friendly. Now it’s time for you to practice these concepts. Head over to the practice exercises to solidify your understanding and gain hands-on experience with implementing sorting.