Advertisement
  1. Code
  2. Coding Fundamentals
  3. Databases & SQL

Creating a Web App From Scratch Using Python Flask and MySQL: Part 5

Scroll to top
Read Time: 13 min
This post is part of a series called Creating a Web App From Scratch Using Python Flask and MySQL.
Creating a Web App From Scratch Using Python Flask and MySQL: Part 4
Creating a Web App From Scratch Using Python Flask and MySQL: Part 6

In the previous part of this series, we saw how to implement the Edit and Delete wish functionality for our Bucket List Application. In this part we'll implement the paging functionality for our user home list.

Getting Started

Let's start by cloning the previous part of the tutorial from GitHub.

1
git clone https://github.com/jay3dec/PythonFlaskMySQLApp_Part4.git

Once the source code has been cloned, navigate to the project directory and start the web server. 

1
cd PythonFlaskMySQLApp_Part4
2
python app.py

Point your browser to http://localhost:5002/ and you should have the application running.

Implementing Pagination

As the list of wishes on the user home page increases, it gets scrolled down the page. So it's important to implement pagination. We'll be limiting the number of items shown on a page to a certain number. 

Modify the Get Wish Procedure

We'll start by modifying the sp_GetWishByUser procedure to return results based on a limit and offset value. This time we'll be creating our stored procedure statement dynamically to return the result set based on the limit and offset value. Here is the modified sp_GetWishByUser MySQL stored procedure.

1
USE `BucketList`;
2
DROP procedure IF EXISTS `sp_GetWishByUser`;
3
4
DELIMITER $$
5
USE `BucketList`$$
6
CREATE DEFINER=`root`@`localhost` PROCEDURE `sp_GetWishByUser`(
7
IN p_user_id bigint,
8
IN p_limit int,
9
IN p_offset int
10
)
11
BEGIN
12
    SET @t1 = CONCAT( 'select * from tbl_wish where wish_user_id = ', p_user_id, ' order by wish_date desc limit ',p_limit,' offset ',p_offset);
13
	PREPARE stmt FROM @t1;
14
	EXECUTE stmt;
15
	DEALLOCATE PREPARE stmt1;
16
END$$
17
18
DELIMITER ;
19

As seen in the above stored procedure, we created our dynamic SQL query and executed it to get the wish list based on the offset and limit parameters.

Adding Pagination to the UI

First, let's define a few default settings. In app.py add a variable for page limit.

1
# Default setting

2
pageLimit = 2

Make the getWish python method accept POST requests.

1
@app.route('/getWish',methods=['POST'])

Read the offset and limit inside the getWish method and pass it on while calling the MySQL stored procedure sp_GetWishByUser

1
 _limit = pageLimit
2
 _offset = request.form['offset']
3
4
5
con = mysql.connect()
6
cursor = con.cursor()
7
cursor.callproc('sp_GetWishByUser',(_user,_limit,_offset))
8
wishes = cursor.fetchall()
9
10

Modify the GetWishes JavaScript function in userHome.html to make it a POST request and pass the offset value.

1
function GetWishes() {
2
    $.ajax({
3
        url: '/getWish',
4
        type: 'POST',
5
        data: {
6
            offset: 0
7
        },
8
        success: function(res) {
9
10
            var wishObj = JSON.parse(res);
11
            $('#ulist').empty();
12
            $('#listTemplate').tmpl(wishObj).appendTo('#ulist');
13
14
        },
15
        error: function(error) {
16
            console.log(error);
17
        }
18
    });
19
}

Save all the changes and restart the server. Sign in using a valid email address and password and you should have only two records displayed on the screen.

User Home with Limited recordsUser Home with Limited recordsUser Home with Limited records

So the database portion is working well. Next, we need to add the pagination UI to the user home page, which will enable the user to navigate across the data.

We'll use the Bootstrap pagination component. Open up userHome.html and add the following HTML code after the #ulist UL. 

1
<nav>
2
    <ul class="pagination">
3
        <li>
4
            <a href="#" aria-label="Previous">
5
                <span aria-hidden="true">&laquo;</span>
6
            </a>
7
        </li>
8
        <li><a href="#">1</a>
9
        </li>
10
        <li><a href="#">2</a>
11
        </li>
12
        <li><a href="#">3</a>
13
        </li>
14
        <li><a href="#">4</a>
15
        </li>
16
        <li><a href="#">5</a>
17
        </li>
18
        <li>
19
            <a href="#" aria-label="Next">
20
                <span aria-hidden="true">&raquo;</span>
21
            </a>
22
        </li>
23
    </ul>
24
</nav>

Save the changes and restart the server. After successfully signing in, you should be able to see the pagination under the wish list.

Pagination in User Home PagePagination in User Home PagePagination in User Home Page

Making Pagination Dynamic

The above pagination is how our pagination will look. But to make it functional, we need to create our pagination dynamically based on the number of records in the database.

To create our pagination, we'll need the total number of records available in the database. So let's modify the MySQL stored procedure sp_GetWishByUser to return the total number of records available as an out parameter.

1
USE `BucketList`;
2
DROP procedure IF EXISTS `sp_GetWishByUser`;
3
4
DELIMITER $$
5
USE `BucketList`$$
6
CREATE DEFINER=`root`@`localhost` PROCEDURE `sp_GetWishByUser`(
7
IN p_user_id bigint,
8
IN p_limit int,
9
IN p_offset int,
10
out p_total bigint
11
)
12
BEGIN
13
    
14
	select count(*) into p_total from tbl_wish where wish_user_id = p_user_id;
15
16
	SET @t1 = CONCAT( 'select * from tbl_wish where wish_user_id = ', p_user_id, ' order by wish_date desc limit ',p_limit,' offset ',p_offset);
17
	PREPARE stmt FROM @t1;
18
	EXECUTE stmt;
19
	DEALLOCATE PREPARE stmt;
20
END$$
21
22
DELIMITER ;

As seen in the above modified stored procedure, we added a new output parameter called p_total and selected the total count of the wishes based on the user id. 

Also modify the getWish python method to pass an output parameter.

1
 _limit = pageLimit
2
 _offset = request.form['offset']
3
 _total_records = 0
4
5
6
con = mysql.connect()
7
cursor = con.cursor()
8
cursor.callproc('sp_GetWishByUser',(_user,_limit,_offset,_total_records))
9
wishes = cursor.fetchall()
10
11
cursor.close()
12
13
cursor = con.cursor()
14
cursor.execute('SELECT @_sp_GetWishByUser_3');
15
16
outParam = cursor.fetchall()

As you can see in the above code, once we've called the stored procedure we close the cursor and open a new cursor to select the returned out parameter.

Earlier, we were returning a list of wishes from the Python method. Now, we also need to include the total records count in the returned JSON. So we'll make the wish list dictionary into another list and then add the wish list and record count to the main list. Here is the modified code of the getWish python method.

1
response = []
2
wishes_dict = []
3
4
for wish in wishes:
5
    wish_dict = {
6
        'Id': wish[0],
7
        'Title': wish[1],
8
        'Description': wish[2],
9
        'Date': wish[4]}
10
    wishes_dict.append(wish_dict)
11
    
12
response.append(wishes_dict)
13
response.append({'total':outParam[0][0]}) 
14
15
return json.dumps(response)

In the GetWishes JavaScript function, inside the success callback add a console log.

1
console.log(res);

Save all the above changes and restart the server. Sign in using a valid email address and password and when on the user home page, check the browser console. You should be able to see a response similar to the one shown below:

1
[
2
    [{
3
        "Date": "Sun, 15 Feb 2015 15:10:45 GMT",
4
        "Description": "wwe",
5
        "Id": 5,
6
        "Title": "wwe"
7
    }, {
8
        "Date": "Sat, 24 Jan 2015 00:13:50 GMT",
9
        "Description": "Travel to Spain",
10
        "Id": 4,
11
        "Title": "Spain"
12
    }], {
13
        "total": 5
14
    }
15
]

Using the total count received from the response, we can get the total number of pages. 

1
var total = wishObj[1]['total'];
2
var pageCount = total/itemsPerPage;

Dividing the total items count from itemsPerPage count gives us the number of pages required. But this holds true only when the total is a multiple of itemsPerPage. If that's not the case, we'll have to check for that and handle the page count accordingly.

1
var pageRem = total%itemsPerPage;
2
if(pageRem !=0 ){
3
	pageCount = Math.floor(pageCount)+1;
4
}

So that will give us the correct page count.

Now since we have the total number of pages, we'll create the pagination HTML dynamically. Remove the LI element from the pagination HTML we added earlier.

1
<nav>
2
    <ul class="pagination">
3
        // li we'll create dynamically

4
    </ul>

5
</nav>

In the GetWishes success callback, let's create the previous link dynamically using jQuery.

1
var prevLink = $('<li/>').append($('<a/>').attr({
2
        'href': '#'
3
    }, {
4
        'aria-label': 'Previous'
5
    })
6
    .append($('<span/>').attr('aria-hidden', 'true').html('&laquo;')));
7
8
$('.pagination').append(prevLink);

In the above code, we just created the previous button link and appended it to the pagination UL.

Save the above changes and restart the server. On successful sign-in you should be able to see the previous link under the list.

Previous link in the PaginationPrevious link in the PaginationPrevious link in the Pagination

Similarly, let's add the pages in the pagination based on the page count.

1
for (var i = 0; i < pageCount; i++) {
2
    var page = $('<li/>').append($('<a/>').attr('href', '#').text(i + 1));
3
    $('.pagination').append(page);
4
}

Let's also add the Next link after the pages link have been added.

1
var nextLink = $('<li/>').append($('<a/>').attr({
2
        'href': '#'
3
    }, {
4
        'aria-label': 'Next'
5
    })
6
    .append($('<span/>').attr('aria-hidden', 'true').html('&raquo;')));
7
8
$('.pagination').append(nextLink);

Save the changes and restart the server. Sign in using a valid email address and password, and once on the user home page you should be able to see the pagination.

Pagination in User Home PagePagination in User Home PagePagination in User Home Page

Attaching a Click Event to a Page Number

Now comes the main logic that will make our pagination functional. What we're going to do is attach a click event call on each page index to call the GetWishes JavaScript function. Let's first attach a click event to the anchor element displaying the page number.

1
for (var i = 0; i < pageCount; i++) {
2
3
    var aPage = $('<a/>').attr('href', '#').text(i + 1);
4
  
5
    $(aPage).click(function() {
6
        
7
    });
8
  
9
    var page = $('<li/>').append(aPage);
10
    $('.pagination').append(page);
11
12
}

So we just attached an onclick event to the page anchor. On each click we'll call the GetWishes function and pass the offset. So declare the offset outside the for loop.

1
var offset = 0;

Call the GetWishes function inside the click event call.

1
GetWishes(offset);

Also increment the offset based on the number of records shown.

1
offset = offset + 2;

But each time the GetWishes function is called, the value of offset will always be the last one set. So we'll make use of JavaScript Closures to pass the correct offset to the GetWishes function.

1
var offset = 0;
2
3
for (var i = 0; i < pageCount; i++) {
4
5
    var aPage = $('<a/>').attr('href', '#').text(i + 1);
6
  
7
    $(aPage).click(function(offset) {
8
        return function() {
9
            GetWishes(offset);
10
        }
11
    }(offset));
12
  
13
    var page = $('<li/>').append(aPage);
14
    $('.pagination').append(page);
15
    offset = offset + itemsPerPage;
16
17
}

Save all the above changes and restart the server. Sign in using valid credentials and once on the user home page, try clicking the pages in the pagination UL. 

Next, we'll implement the previous and next page links. It may seem a bit complicated, so let me explain it a bit before we start with the implementation. 

We'll be displaying five pages at a time. Using the next and previous link the user can navigate to the next five and previous five pages respectively. We'll store the values of the start page and end page and keep updating both on the next and previous button click. So let's start by adding two hidden fields to the userHome.html page.

1
<input type="hidden" id="hdnStart" value="1" />
2
<input type="hidden" id="hdnEnd" value="5"/>

In the GetWishes success callback, after we have emptied the .pagination UL, add the following line of code to get the latest start page and end page.

1
$('.pagination').empty();
2
3
var pageStart = $('#hdnStart').val();
4
var pageEnd = $('#hdnEnd').val();

No previous button link will be shown when displaying pages 1 to 5. If the pages displayed are greater than 5 then we'll display the previous button link.

1
if (pageStart > 5) {
2
    var aPrev = $('<a/>').attr({
3
            'href': '#'
4
        }, {
5
            'aria-label': 'Previous'
6
        })
7
        .append($('<span/>').attr('aria-hidden', 'true').html('&laquo;'));
8
9
    $(aPrev).click(function() {
10
        // Previous button logic

11
    });
12
13
    var prevLink = $('<li/>').append(aPrev);
14
    $('.pagination').append(prevLink);
15
}

When the user clicks the previous button, we'll reset the hdnStart and hdnEnd values and call the GetWishes JavaScript function. 

1
$(aPrev).click(function() {
2
    $('#hdnStart').val(Number(pageStart) - 5);
3
    $('#hdnEnd').val(Number(pageStart) - 5 + 4);
4
    GetWishes(Number(pageStart) - 5);
5
});

Next, based on the start page and the end page we'll loop and create the page links and append the .pagination UL.

1
for (var i = Number(pageStart); i <= Number(pageEnd); i++) {
2
3
    if (i > pageCount) {
4
        break;
5
    }
6
7
8
    var aPage = $('<a/>').attr('href', '#').text(i);
9
    
10
    // Attach the page click event

11
    $(aPage).click(function(i) {
12
        return function() {
13
            GetWishes(i);
14
        }
15
    }(i));
16
    
17
    var page = $('<li/>').append(aPage);
18
19
    // Attach the active page class

20
    if ((_page) == i) {
21
        $(page).attr('class', 'active');
22
    }
23
24
    $('.pagination').append(page);
25
26
27
}

By comparing the total page count and the page start value, we'll decide the display of the next button link.

1
if ((Number(pageStart) + 5) <= pageCount) {
2
    var nextLink = $('<li/>').append($('<a/>').attr({
3
            'href': '#'
4
        }, {
5
            'aria-label': 'Next'
6
        })
7
        .append($('<span/>').attr('aria-hidden', 'true').html('&raquo;').click(function() {
8
            $('#hdnStart').val(Number(pageStart) + 5);
9
            $('#hdnEnd').val(Number(pageStart) + 5 + 4);
10
            GetWishes(Number(pageStart) + 5);
11
12
        })));
13
    $('.pagination').append(nextLink);
14
}

As seen in the above code, on the next button click we are resetting the hdnStart and hdnEnd button values and calling the GetWishes JavaScript function.

So here is the final GetWishes JavaScript function. 

1
function GetWishes(_page) {
2
3
    var _offset = (_page - 1) * 2;
4
  
5
    $.ajax({
6
        url: '/getWish',
7
        type: 'POST',
8
        data: {
9
            offset: _offset
10
        },
11
        success: function(res) {
12
13
            var itemsPerPage = 2;
14
15
            var wishObj = JSON.parse(res);
16
17
            $('#ulist').empty();
18
            $('#listTemplate').tmpl(wishObj[0]).appendTo('#ulist');
19
20
            var total = wishObj[1]['total'];
21
            var pageCount = total / itemsPerPage;
22
            var pageRem = total % itemsPerPage;
23
            if (pageRem != 0) {
24
                pageCount = Math.floor(pageCount) + 1;
25
            }
26
27
28
            $('.pagination').empty();
29
30
            var pageStart = $('#hdnStart').val();
31
            var pageEnd = $('#hdnEnd').val();
32
33
34
35
36
            if (pageStart > 5) {
37
                var aPrev = $('<a/>').attr({
38
                        'href': '#'
39
                    }, {
40
                        'aria-label': 'Previous'
41
                    })
42
                    .append($('<span/>').attr('aria-hidden', 'true').html('&laquo;'));
43
44
                $(aPrev).click(function() {
45
                    $('#hdnStart').val(Number(pageStart) - 5);
46
                    $('#hdnEnd').val(Number(pageStart) - 5 + 4);
47
                    GetWishes(Number(pageStart) - 5);
48
                });
49
50
                var prevLink = $('<li/>').append(aPrev);
51
                $('.pagination').append(prevLink);
52
            }
53
54
55
56
            for (var i = Number(pageStart); i <= Number(pageEnd); i++) {
57
58
                if (i > pageCount) {
59
                    break;
60
                }
61
62
63
                var aPage = $('<a/>').attr('href', '#').text(i);
64
65
                $(aPage).click(function(i) {
66
                    return function() {
67
                        GetWishes(i);
68
                    }
69
                }(i));
70
                var page = $('<li/>').append(aPage);
71
72
                if ((_page) == i) {
73
                    $(page).attr('class', 'active');
74
                }
75
76
                $('.pagination').append(page);
77
78
79
            }
80
            if ((Number(pageStart) + 5) <= pageCount) {
81
                var nextLink = $('<li/>').append($('<a/>').attr({
82
                        'href': '#'
83
                    }, {
84
                        'aria-label': 'Next'
85
                    })
86
                    .append($('<span/>').attr('aria-hidden', 'true').html('&raquo;').click(function() {
87
                        $('#hdnStart').val(Number(pageStart) + 5);
88
                        $('#hdnEnd').val(Number(pageStart) + 5 + 4);
89
                        GetWishes(Number(pageStart) + 5);
90
91
                    })));
92
                $('.pagination').append(nextLink);
93
            }
94
95
96
97
98
        },
99
        error: function(error) {
100
            console.log(error);
101
        }
102
    });
103
}

Save all the above changes and restart the server. Sign in using a valid email address and password. You should be able to see the fully functional pagination for the user wish list.

Conclusion

In this part of series, we implemented the pagination functionality for the wish list on the user home page. We saw how to retrieve data using a MySQL stored procedure and create pagination using that data, jQuery and Bootstrap.

In the next part of this tutorial series, we'll implement the file upload functionality into our application.

Source code from this tutorial is available on GitHub.

Do let us know your thoughts in the comments below! 

Advertisement
Did you find this post useful?
Want a weekly email summary?
Subscribe below and we’ll send you a weekly email summary of all new Code tutorials. Never miss out on learning about the next big thing.
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.