Part 3: Building a User-Friendly Todo App with Flask - A Step-by-Step Guide to Performing GET Operations and Rendering Data on HTML Pages

Hey guys, welcome to part three of our Building a User-Friendly Todo App with Flask! In this installment, we're diving into performing GET operations in our Flask application. In this video, I'll show you how to execute GET requests to our Flask backend, which in turn queries our database to return the list of todos and categories, and then display them on the HTML frontend for users to see. Let's get started!

To begin, first, copy the HTML code below and replace the content in your index.html 

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <title>Todo App</title>
  <link href="https://cdn.jsdelivr.net/npm/tailwindcss@2.2.19/dist/tailwind.min.css" rel="stylesheet">
  <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.15.4/css/all.min.css" rel="stylesheet">
</head>
<body class="bg-gray-100">

  <div class="container mx-auto py-8">
    <h1 class="text-3xl font-semibold text-center mb-6">Flask Todo App</h1>

    <!-- Todo Add Section Starts -->
    <form action="/todos" method="post" class=" mx-auto bg-white p-6 rounded-md shadow-md">
      <input required type="text" name="description" id="todoInput" class="w-full px-4 py-2 border border-gray-300 rounded-md mb-4 focus:outline-none" placeholder="Add a new todo..">
      <select name="category" class="p-2  focus:outline-none mt-5 mb-5 w-full border-red-400 rounded-md ">
        <option disabled>Select Category</option>
        <option value="important">Important</option>
        <option value="urgent">Urgent</option>
      </select>
      <button type="submit" class="w-full p-2 bg-blue-400 text-white font-semibold">Add Todo</button>
    </form>
    <!-- Todo Add Section Ends -->    

    <div class="grid grid-cols-6 mt-5 gap-4">
        <div class="col-span-2 bg-white rounded-md shadow-md p-3">
            <h3 class="font-semibold text-lg text-center">Categories</h3>

            <!-- Category Add Section Starts -->
            <form action="/categories" method="post" class="max-w-md mx-auto bg-white p-6 rounded-md">
                <input required type="text" name="categoryname" id="categoryname" class="w-full px-4 py-2 border border-gray-300 rounded-md mb-4 focus:outline-none" placeholder="Add a new category..">
                <button class="w-full p-2 bg-blue-400 text-white font-semibold">Add</button>
            </form>
            <!-- Category Add Section Ends -->

            <ul class="text-md mt-5 p-5 gap-3">
                <div class="flex justify-between w-full">
                    <li class="p-2 mb-3"><a href="/" class="text-white p-2 w-full bg-blue-400">All</a></li>
                </div>

                <!-- Category List Section Starts -->
                <li class="p-2 mb-3 flex gap-4">
                    <a href="#" id="cat_5" class="p-2 w-full text-blue-400">Sample Category</a>
                    <span class="flex items-center">
                        <i class="fas fa-edit edit-icon text-gray-500 mr-3 cursor-pointer" title="Edit"></i>
                        <button>
                            <i class="fas fa-trash delete-icon text-red-500 cursor-pointer" title="Delete" data-todo-id="cat_5"></i>
                        </button>  
                    </span>  
                </li>
                <!-- Category List Section Ends -->
            </ul>
        </div>

        <div class="col-span-4">
            <ul class="todo-list">
                <!-- Todo items will be added here -->
                <!-- Todo List Section Starts -->
                <li class="flex justify-between items-center py-2 border-b border-gray-200">
                    <span class="text-lg">Create Articles</span>
                    <span class="flex items-center">
                        <i class="fas fa-edit edit-icon text-gray-500 mr-3 cursor-pointer" title="Edit"></i>
                        <button>
                            <i class="fas fa-trash delete-icon text-red-500 cursor-pointer" title="Delete" data-todo-id="cat_3"></i>
                        </button>  
                    </span>
                </li>
                <!-- Todo List Section Ends -->
            </ul>
        </div>
    </div>
  </div>
<script>
function makeEditable() {
        
      }
  
      function updateTodoDescription() {
        // Perform an API request to update the todo description
        
      }
    
      function deleteTodo() {
        
        
      }



      function makeCategoryEditable() {
        
      }
  
      function updateCategoryName() {
        // Perform an API request to update the todo description
        
      }
    
      function deleteCategory() {
        
        

      }
</script>

</body>
</html>

his HTML code creates the basic UI for the todo application that I showed you in part one of this series.

Next, go to our pgAdmin4 database and add a couple of dummy todos and categories (refer to the video at the end of this post if you're unsure how to do that). Remember, you must add a category before you can add a todo because each todo references a category via a foreign key relationship.

Once done, head to your app.py file and in the index route that we created earlier, query your database to return the list of todos and categories you have in your database, as shown below:

def index():
    category_param = request.args.get('category')  # Retrieves the 'category' parameter from the request URL query string
    global todos  # Declares 'todos' as a global variable to be accessed and modified within the function
    global current_category  # Declares 'current_category' as a global variable

    if category_param:  # Checks if a 'category' parameter exists in the request URL
        todos = Todo.query.filter_by(category=category_param).all()  # Retrieves all Todo items filtered by the category parameter
        current_category = int(category_param)  # Sets the current category to the integer value of the category parameter
    else:  # Executes if no 'category' parameter is present in the request URL
        todos = Todo.query.all()  # Retrieves all Todo items
        current_category = "All"  # Sets the current category to "All"

    categories = Category.query.all()  # Retrieves all categories from the database

    # Renders the 'index.html' template with the retrieved data (todos, categories, current_category)
    return render_template('index.html', todos=todos, categories=categories, current_category=current_category)

return render_template sends the todo and category data to the index.html page as specified in the code

Start your python flask application and go to the root page then go back to your terminal you should see your list  of todos and categories printed out.

Now go to your index.html file and edit the Todo list section, by replacing its current content with this

<!-- Todo items will be added here -->
<!-- Todo List Section Starts -->
{% for todo in todos %}
            <li class="flex justify-between items-center py-2 border-b border-gray-200">
              <span id="todoDescription_{{ todo.id }}" class="text-lg">{{ todo.description }}</span>
              <span class="flex items-center">
                <i onclick="makeEditable({{ todo.id }})" class="fas fa-edit edit-icon text-gray-500 
                  mr-3 cursor-pointer" title="Edit"></i>
                <button onclick="deleteTodo({{todo.id}})">
                <i class="fas fa-trash delete-icon text-red-500 cursor-pointer" title="Delete" data- 
                 todo-id="{{todo.id}}"></i>
                </button>  
            </span>
            </li>
{% endfor %}

 <!-- Todo List Section Ends -->

This code loops through all the todos passed from our app.py index route and displays them on the UI. The new HTML contains some onclick functions to enable the deletion and editing of the articles. These functions were created within the script tag, and we'll get to that in the next part of this series.

Refresh your page, and you should see the list of todos you added in your database.

Similarly, replace the content of the Categories list section in your index.html file with the provided code snippet:

<ul class="text-md mt-5 p-5 gap-3">
                    <li class="{{ 'p-2 mb-3' if current_category == 'All' else 'p-2 mb-3' }}">
                        <a href="/" class="{{ 'text-white bg-blue-400 p-2 w-full' if current_category == 'All' else 'text-blue-400 p-2 w-full' }}">All</a>
                    </li>


                    {% for category in categories %}
                    <li class="{{ 'text-white p-2 flex mb-3 gap-4' if current_category == category.id else 'p-2 mb-3 flex gap-4' }}">
                        <a href="/?category={{ category.id }}" id="category_{{ category.id }}" class="{{ 'text-white p-2 w-full bg-blue-400' if current_category == category.id else 'p-2 w-full text-blue-400' }}">
                            {{ category.name }}
                        </a>
                        <span class="flex items-center">
                            <i onclick="makeCategoryEditable({{ category.id }})" class="fas fa-edit edit-icon text-gray-500 mr-3 cursor-pointer" title="Edit"></i>
                            <button onclick="deleteCategory({{ category.id }})">
                                <i class="fas fa-trash delete-icon text-red-500 cursor-pointer" title="Delete" data-todo-id="{{ category.id }}"></i>
                            </button>  
                        </span>
                    </li>
{% endfor %}
        
</ul>

Here is a breakdown of the codesnippet above

Looping through Categories:

{% for category in categories %}: Initiates a loop that goes through each category in the categories list.


Conditional Styling:

class="{{ 'text-white p-2 flex mb-3 gap-4' if current_category == category.id else 'p-2 mb-3 flex gap-4' }}":
Sets the class of the <li> element based on a condition.
If current_category matches category.id, it applies styles for a selected category (e.g., text-white, bg-blue-400 for background, etc.). Otherwise, it applies styles for unselected categories.


Dynamic Attributes and Conditional Classes:

href="/?category={{ category.id }}": Sets the href attribute dynamically for the anchor (<a>) tag based on the category.id.
id="category_{{ category.id }}": Dynamically assigns an id to the anchor element.


Conditional Classes for Anchor Tag:

class="{{ 'text-white p-2 w-full bg-blue-400' if current_category == category.id else 'p-2 w-full text-blue-400' }}":
Sets the class for the <a> tag based on the condition similar to the <li> class.
If current_category matches category.id, it applies styles for a selected category. Otherwise, it applies styles for unselected categories.


Edit and Delete Functionality:

The <i> tag inside the <span> tag is assigned an onclick event for editing (makeCategoryEditable) and a button for deleting (deleteCategory) based on the category.id.

After refreshing the page, you'll witness the list of categories you've added to your database. Clicking on any available category will dynamically update your todo list based on the selected category. Choosing the 'All' category will display all categories stored in your database.

That concludes this part of the series. To watch a detailed step-by-step demonstration, refer to the video below. Click on the link to proceed to the next part where we'll focus on sending data from our category and todo forms to our Flask backend route and subsequently into our database.

 

 

 

Author
author-image

Hello, my name is Abubakar Zakari. Am a budding fullstack developer from Nigeria who loves developing softwares and learning new frameworks and langauges.

Comment

Select image


Comments
No comment yet

DEVMAESTERS

Newsletter

Services

Frontend Development |Backend Development |Full Website Development |Bootstrap Website upgrades | Website Debbugging | Website Hosting & deployment

Contact

Interested in hiring me or collaborating with me on a project, click on any of the links below to get my social media handle

Or contact me via Tel: (+234)-806-225-7480 | Email: abubakarzakari1703@gmail.com

Copywright@devmaesters.com
Privacy Policy

By using our website,
you agree that devmaesters can store cookies on your device and disclose information in accordance with our privacy policy.