Advertisement
  1. Code
  2. Python

Tạo một Ứng dụng Web từ đầu Sử dụng Python Flask và MySQL: Phần 5

Scroll to top
Read Time: 15 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

Vietnamese (Tiếng Việt) translation by Dai Phong (you can also view the original English article)

Trong phần trước của loạt bài này, chúng tôi đã giới thiệu cách cài đặt chức năng Chỉnh sửaXóa mong ước cho Ứng dụng Danh sách Mong ước của chúng ta. Trong phần này, chúng ta sẽ cài đặt chức năng phân trang cho danh sách trên trang chủ của người dùng.

Bắt đầu

Hãy bắt đầu bằng cách nhân bản mã nguồn từ phần trước của hướng dẫn trên GitHub.

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

Một khi mã nguồn đã được nhân bản, chuyển đến thư mục dự án và khởi động máy chủ web.

1
cd PythonFlaskMySQLApp_Part4
2
python app.py

Trỏ trình duyệt của bạn đến http://localhost:5002/ và bạn sẽ thấy ứng dụng đang chạy.

Cài đặt Phân trang

Khi danh sách mong ước trên trang chủ của người dùng tăng lên, nó sẽ được cuộn xuống dưới trang. Vì vậy điều quan trọng là phải cài đặt việc phân trang. Chúng ta sẽ hạn chế số lượng phần tử nhất định được hiển thị trên một trang.

Chỉnh sửa Thủ tục Lấy Mong ước

Chúng ta sẽ bắt đầu bằng cách sửa đổi thủ tục sp_GetWishByUser để trả về kết quả dựa trên giá trị limitoffset. Lần này chúng ta sẽ tạo ra thủ tục lưu trữ tự động để trả về bộ kết quả dựa trên giá trị limit và offset. Đây là thủ tục lưu trữ sp_GetWishByUser trong MySQL đã được sửa đổi.

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

Như đã thấy trong thủ tục lưu trữ ở trên, chúng ta tạo ra truy vấn SQL động của chúng ta và thực thi nó để lấy danh sách mong ước dựa trên các tham số offsetlimit.

Thêm Phân trang vào Giao diện Người dùng

Trước tiên, hãy định nghĩa một vài thiết lập mặc định. Trong app.py thêm một biến cho giới hạn trang.

1
# Default setting

2
pageLimit = 2

Làm cho phương thức getWish chấp nhận các yêu cầu POST.

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

Đọc offsetlimit bên trong phương thức getWish và truyền nó trong khi gọi thủ tục lưu trữ MySQL 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

Sửa đổi hàm JavaScript GetWishes trong userHome.html để thiết lập một yêu cầu POST và truyền vào giá trị offset.

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
}

Lưu tất cả các thay đổi và khởi động lại máy chủ. Đăng nhập bằng một địa chỉ email và mật khẩu hợp lệ và bạn sẽ có có hai phần tử được hiển thị trên màn hình.

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

Vì vậy phần cơ sở dữ liệu đang hoạt động tốt. Tiếp theo, chúng ta cần thêm giao diện phân trang vào trang chủ của người dùng, điều này sẽ cho phép người dùng điều hướng qua lại giữa các trang.

Chúng ta sẽ sử dụng thành phần phần trang (gọi là pagination) của Bootstrap. Mở userHome.html và thêm code HTML sau đây sau UL #ulist.

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>

Lưu các thay đổi và khởi động lại máy chủ. Sau khi đăng nhập thành công, bạn sẽ có thể thấy số trang ở dưới danh sách mong ước.

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

Tạo Phần trang Động

Phần trang ở trên là hình ảnh cách đánh số trang của chúng ta. Nhưng để làm cho nó hoạt động, chúng ta cần phải tạo phân nhóm của chúng một cách tự động dựa trên số lượng các phần tử trong cơ sở dữ liệu.

Để tạo số trang, chúng ta sẽ cần tổng số phần tử có trong cơ sở dữ liệu. Vì vậy, hãy thay đổi thủ tục lưu trữ MySQL sp_GetWishByUser để trả về tổng số các phần tử trong đó như là một tham số đầu ra.

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 ;

Như đã thấy trong thủ tục lưu trữ đã sửa đổi ở trên, chúng ta đã thêm một tham số đầu ra mới gọi là p_total và chọn tổng số các mong ước dựa trên id của người dùng.

Đồng thời sửa đổi phương thức pythong getWish để truyền vào một tham số đầu ra.

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()

Như bạn thấy trong đoạn code ở trên, một khi chúng ta đã gọi thủ tục lưu trữ thì chúng ta đóng con trỏ lại và mở một con trỏ mới để chọn tham số đầu ra được trả về.

Trước đó, chúng ta đã trả về một danh sách mong ước từ phương thức của Python. Bây giờ, chúng ta cũng cần bao gồm tổng số phần tử đếm được trong JSON trả về. Vì vậy, chúng ta sẽ làm cho dictionary của danh sách mong ước thành một danh sách khác và sau đó thêm danh sách mong ước và tổng số phần tử vào danh sách chính. Dưới đây là code đã sửa đổi của phương thức getWish trong python.

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)

Trong hàm GetWishes của JavaScript, bên trong hàm callback success, thêm một log console.

1
console.log(res);

Lưu tất cả các thay đổi ở trên và khởi động lại máy chủ. Đăng nhập bằng một địa chỉ email và mật khẩu hợp lệ và khi ở trên trang chủ của người dùng, hãy kiểm tra giao diện console của trình duyệt. Bạn sẽ thấy phản hồi tương tự như hình dưới đây:

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
]

Sử dụng tổng số nhận được từ phản hồi, chúng ta có thể lấy được tổng số trang.

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

Chia tổng số phần tử cho itemsPerPage cho ra số trang cần thiết. Nhưng điều này chỉ đúng khi tổng là một bội số của itemPerPage. Nếu không, chúng ta sẽ phải kiểm tra và xử lý số trang cho phù hợp.

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

Như vậy, điều đó sẽ cho chúng ta số trang đúng.

Bây giờ nhờ chúng ta có tổng số trang, nên chúng ta sẽ tạo HTML phân trang một cách tự động. Xóa phần tử LI khỏi HTML phân trang mà chúng ta đã thêm vào trước đó.

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

4
    </ul>

5
</nav>

Trong hàm callback success GetWishes, hãy tạo liên kết previous động bằng 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);

Trong đoạn code ở trên, chúng ta tạo ra button previous và nối nó vào phân trang UL.

Lưu thay đổi ở trên và khởi động lại máy chủ. Khi đăng nhập thành công, bạn sẽ có thể thấy liên kết previous trong danh sách.

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

Tương tự, hãy thêm các trang trong phân trang dựa theo số trang.

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

Hãy thêm liên kết Next sau khi liên kết các trang đã được thêm.

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);

Lưu các thay đổi và khởi động lại máy chủ. Đăng nhập bằng một địa chỉ email và mật khẩu hợp lệ, và một khi ở trên trang chủ của người dùng, bạn sẽ có thể nhìn thấy số trang.

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

Cài đặt Sự kiện Nhấp vào Số Trang

Bây giờ đi vào logic chính sẽ làm cho chức năng phân trang của chúng ta hoạt động. Những gì chúng ta sẽ làm là gắn một sự kiện click trên mỗi chỉ mục trang để gọi hàm GetWishes của JavaScript. Đầu tiên hãy gắn một sự kiện click vào phần tử liên kết hiển thị số trang.

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
}

Vì vậy, chúng ta chỉ cần gắn một sự kiện onclick vào liên kết trang. Trong mỗi lần nhấp chuột chúng ta sẽ gọi hàm GetWishes và truyền vào offset. Vì vậy, hãy khai báo offset bên ngoài vòng lặp for.

1
var offset = 0;

Gọi hàm GetWishes bên trong sự kiện click.

1
GetWishes(offset);

Đồng thời tăng dần offset dựa trên số lượng các phần tử được hiển thị.

1
offset = offset + 2;

Nhưng mỗi khi hàm GetWishes được gọi, giá trị của offset sẽ luôn luôn giá trị được thiết lập gần nhất. Vì vậy, chúng ta sẽ sử dụng tính năng Closure của JavaScript để truyền offset phù hợp cho hàm GetWishes.

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
}

Lưu tất cả các thay đổi ở trên và khởi động lại máy chủ. Đăng nhập bằng các thông tin xác thực hợp lệ và một khi ở trên trang chủ của người dùng, hãy thử nhấp vào các trang trong phân trang UL.

Tiếp theo, chúng ta sẽ cài đặt các liên kết trang trước (previous) và trang sau (next). Nó có vẻ hơi phức tạp một chút, vì vậy hãy để tôi giải thích nó trước khi chúng ta bắt đầu cài đặt.

Chúng ta sẽ hiển thị năm trang mỗi lần. Sử dụng liên kết next và previous, người dùng có thể điều hướng tương ứng đến 5 trang sau và 5 trang trước. Chúng ta sẽ lưu trữ các giá trị của trang đầu và trang cuối và tiếp tục cập nhật cả hai khi bấm nút next và previous. Vì vậy, hãy bắt đầu bằng cách thêm hai trường ẩn vào trang userHome.html.

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

Trong hàm callback success GetWishes, sau khi chúng ta đã làm rỗng UL .pagination, hãy thêm dòng code sau đây để có được trang đầu và trang cuối mới nhất.

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

Không có button previous hiển thị khi hiển thị các trang từ 1 đến 5. Nếu các trang được hiển thị lớn hơn 5 thì chúng ta sẽ hiển thị nút previous.

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
}

Khi người dùng nhấp vào nút previous, chúng ta sẽ reset các giá trị hdnStarthdnEnd và gọi hàm GetWishes của JavaScript.

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

Tiếp theo, dựa trên trang đầu và trang cuối, chúng ta sẽ lặp và tạo ra các liên kết trang và nối vào UL .pagination.

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
}

Bằng cách so sánh tổng số trang và giá trị bắt đầu của trang, chúng ta sẽ quyết định việc hiển thị button next.

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
}

Như đã thấy trong đoạn code ở trên, trên button next, chúng ta sẽ reset các giá trị hdnStarthdnEnd và gọi hàm GetWishes của JavaScript.

Như vậy đây là hàm GetWishes hoàn chỉnh.

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
}

Lưu tất cả các thay đổi ở trên và khởi động lại máy chủ. Đăng nhập bằng một địa chỉ email và mật khẩu hợp lệ. Bạn sẽ có thể nhìn thấy phân trang đầy đủ chức năng cho danh sách mong ước của người dùng.

Phần tóm tắt

Trong phần này của loạt bài, chúng ta đã cài đặt chức năng phân trang cho danh sách mong ước trên trang chủ của người dùng. Chúng ta đã học được cách để lấy dữ liệu bằng một thủ tục lưu trữ MySQL và tạo phân trang bằng cách sử dụng dữ liệu đó, jQuery và Bootstrap.

Trong phần tiếp theo của loạt bài hướng dẫn này, chúng ta sẽ cài đặt chức năng tải tập tin lên ứng dụng của chúng ta.

Mã nguồn từ hướng dẫn này có sẵn trên GitHub.

Hãy cho chúng tôi biết những suy nghĩ của bạn trong phần bình luận ​​dưới đây nhé!

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.