() translation by (you can also view the original English article)
Trong phần trước của loạt bài này, chúng ta đã cài đặt phân trang cho danh sách mong ước trên trang chủ của người dùng. Trong phần này, chúng ta sẽ cài đặt một tùy chọn để người dùng tải lên một hình ảnh đại diện cho mong ước, một tuỳ chọn để đánh dấu mong ước đã hoàn thành và một tùy chọn để thiết lập riêng tư.
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_Part5.git |
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_Part5
|
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.
Chỉnh sửa Giao diện Người dùng
Hãy bắt đầu bằng cách chỉnh sửa trang "add wish" của chúng ta để bao gồm một tùy chọn tải lên một tấm ảnh. Chuyển đến templates/addWish.html
. Form của chúng ta trong addWish.html
có vẻ khá nhỏ, vì vậy, hãy sửa đổi code HTML bootstrap để làm cho form thẳng đứng.
Trước tiên, chúng ta sẽ sửa form-horizontal
thành một form thẳng đứng, do đó hãy xoá lớp form-horizontal
ra khỏi form. Chúng ta cũng sẽ thêm ba control mới: control để tải lên các tấm ảnh, hộp kiểm để đánh dấu mong ước là riêng tư và một hộp kiểm khác để đánh dấu mong ước đã hoàn thành. Đây là addWish.html
đã được chỉnh sửa.
1 |
<!DOCTYPE html>
|
2 |
<html lang="en"> |
3 |
|
4 |
<head>
|
5 |
<title>Python Flask Bucket List App</title> |
6 |
|
7 |
|
8 |
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.2/css/bootstrap.min.css"> |
9 |
|
10 |
<link href="http://getbootstrap.com/examples/jumbotron-narrow/jumbotron-narrow.css" rel="stylesheet"> |
11 |
|
12 |
<script src="../static/js/jquery-1.11.2.js"></script> |
13 |
<style>
|
14 |
.btn-file { |
15 |
position: relative; |
16 |
overflow: hidden; |
17 |
}
|
18 |
|
19 |
.btn-file input[type=file] { |
20 |
position: absolute; |
21 |
top: 0; |
22 |
right: 0; |
23 |
min-width: 100%; |
24 |
min-height: 100%; |
25 |
font-size: 100px; |
26 |
text-align: right; |
27 |
filter: alpha(opacity=0); |
28 |
opacity: 0; |
29 |
outline: none; |
30 |
background: white; |
31 |
cursor: inherit; |
32 |
display: block; |
33 |
}
|
34 |
</style>
|
35 |
|
36 |
</head>
|
37 |
|
38 |
<body>
|
39 |
|
40 |
<div class="container"> |
41 |
<div class="header"> |
42 |
<nav>
|
43 |
<ul class="nav nav-pills pull-right"> |
44 |
<li role="presentation" class="active"><a href="#">Add Item</a> |
45 |
</li>
|
46 |
<li role="presentation"><a href="/logout">Logout</a> |
47 |
</li>
|
48 |
</ul>
|
49 |
</nav>
|
50 |
<h3 class="text-muted">Python Flask App</h3> |
51 |
</div>
|
52 |
|
53 |
<form role="form" method="post" action="/addWish"> |
54 |
|
55 |
|
56 |
<!-- Form Name -->
|
57 |
<legend>Create Your Wish</legend> |
58 |
|
59 |
<!-- Text input-->
|
60 |
<div class="form-group"> |
61 |
<label for="txtTitle">Title</label> |
62 |
|
63 |
<input id="txtTitle" name="inputTitle" type="text" placeholder="placeholder" class="form-control input-md"> |
64 |
|
65 |
</div>
|
66 |
|
67 |
<!-- Textarea -->
|
68 |
<div class="form-group"> |
69 |
<label for="txtPost">Description</label> |
70 |
|
71 |
<textarea class="form-control" id="txtPost" name="inputDescription"></textarea> |
72 |
|
73 |
</div>
|
74 |
|
75 |
|
76 |
<div class="form-group"> |
77 |
<label for="txtPost">Photos</label> |
78 |
|
79 |
<div class="input-group"> |
80 |
<span class="input-group-btn"> |
81 |
<span class="btn btn-primary btn-file"> |
82 |
Browse… <input type="file" id="fileupload" name="file" multiple> |
83 |
</span>
|
84 |
</span>
|
85 |
<input type="text" class="form-control" readonly> |
86 |
</div>
|
87 |
|
88 |
</div>
|
89 |
|
90 |
<div class="form-group"> |
91 |
<label>Mark this as private and not visible to others.</label> |
92 |
<br/>
|
93 |
<input type="checkbox"> Mark as Private <span class="glyphicon glyphicon-lock" aria-hidden="true"></span> |
94 |
</div>
|
95 |
|
96 |
<div class="form-group"> |
97 |
<label>Have you already accomplished this?</label> |
98 |
<br/>
|
99 |
<input type="checkbox"> Mark as Done <span class="glyphicon glyphicon-ok" aria-hidden="true"></span> |
100 |
</div>
|
101 |
|
102 |
|
103 |
|
104 |
|
105 |
<!-- Button -->
|
106 |
<div class="form-group"> |
107 |
|
108 |
<p class="text-center"> |
109 |
<input id="singlebutton" name="singlebutton" class="btn btn-primary" type="submit" value="Publish" /> |
110 |
</p>
|
111 |
</div>
|
112 |
|
113 |
|
114 |
</form>
|
115 |
|
116 |
<footer class="footer"> |
117 |
<p>© Company 2015</p> |
118 |
</footer>
|
119 |
|
120 |
</div>
|
121 |
</body>
|
122 |
|
123 |
</html>
|
Lưu thay đổi ở trên và khởi động lại máy chủ. Sau khi đăng nhập thành công, nhấp chuột vào liên kết Add Wish và bạn sẽ có thể thấy trang add wish đã được sửa đổi.



Cài đặt Chức năng Tải lên
Chúng ta sẽ sử dụng jQuery-File-Upload của blueimp để cài đặt chức năng tải lên tập tin. Tải về các tập tin cần thiết từ GitHub. Trích xuất mã nguồn và thêm các cham chiếu script sau vào addWish.html
.
1 |
<script src="../static/js/jquery-1.11.2.js"></script> |
2 |
|
3 |
<script src="../static/js/jquery.ui.widget.js"></script> |
4 |
|
5 |
<script type="text/javascript" src="../static/js/jquery.fileupload.js"></script> |
6 |
|
7 |
<script type="text/javascript" src="../static/js/jquery.fileupload-process.js"></script> |
8 |
|
9 |
<script type="text/javascript" src="../static/js/jquery.fileupload-ui.js"></script> |
Khi trang addWish.html
được tải, thêm code khởi tạo plugin vào sự kiện click của button tải lên.
1 |
$(function() { |
2 |
$('#fileupload').fileupload({ |
3 |
url: 'upload', |
4 |
dataType: 'json', |
5 |
add: function(e, data) { |
6 |
data.submit(); |
7 |
},
|
8 |
success: function(response, status) { |
9 |
console.log(response); |
10 |
},
|
11 |
error: function(error) { |
12 |
console.log(error); |
13 |
}
|
14 |
});
|
15 |
})
|
Như đã thấy trong đoạn code ở trên, chúng ta đã gắn plugin tải tập tin lên vào button #fileupload
. Plugin tải lên tập tin post tập tin đến trình xử lý yêu cầu /upload
, cái mà chúng ta sẽ định nghĩa trong code Python của chúng ta. Chúng ta cũng đã định nghĩa một hàm add
để submit dữ liệu và định nghĩa các callback success
và failure
để xử lý tải lên thành công và thất bại.
Tiếp theo, hãy định nghĩa trình xử ly tải lên tập tin Python upload
trong app.py
. Xác định một tuyến /upload
như sau:
1 |
@app.route('/upload', methods=['GET', 'POST']) |
2 |
def upload(): |
3 |
# file upload handler code will be here
|
Kiểm tra xem yêu cầu có phải là một yêu cầu POST
hay không, và nếu như vậy đọc tập tin từ yêu cầu.
1 |
if request.method == 'POST': |
2 |
file = request.files['file'] |
Chúng ta cũng cần phải lấy phần mở rộng tập tin hình ảnh để lưu tập tin. Vì vậy, hãy import os
và sau đó chia tách phần mở rộng khỏi tên tập tin.
1 |
extension = os.path.splitext(file.filename)[1] |
Một khi chúng ta có phần mở rộng tập tin, chúng ta sẽ tạo ra một tên tập tin duy nhất mới bằng uuid
. Import uuid
và tạo tên tập tin.
1 |
f_name = str(uuid.uuid4()) + extension |
Tạo một thư mục có tên là Uploads
trong thư mục static. Đây là nơi chúng ta sẽ giữ các hình ảnh đã tải lên. Thêm đường dẫn đến thư mục Uploads trong cấu hình ứng dụng.
1 |
app.config['UPLOAD_FOLDER'] = 'static/Uploads' |
Bây giờ hãy lưu tập tin đã được post lên vào vị trí UPLOAD_FOLDER
và trả về tên tập tin như một phản hồi (response).
1 |
file.save(os.path.join(app.config['UPLOAD_FOLDER'], f_name)) |
2 |
return json.dumps({'filename':f_name}) |
Lưu các thay đổi ở trên và khởi động lại máy chủ. Trỏ trình duyệt đến http://localhost:5002 và đăng nhập bằng các thông tin xác thực hợp lệ. Hãy thử tải lên một tấm ảnh bằng cách sử dụng button browse và khi hoàn tất, hãy kiểm tra giao diện console trong trình duyệt của bạn. Bạn sẽ có thể thấy tên tập tin tải lên được trả về.
Thay vì input văn bản chỉ-đọc, hãy thêm một phần tử hình ảnh để hiển thị hình ảnh đã tải lên. Vì vậy, hãy thay thế input văn bản chỉ-đọc bằng code HTML sau đây:
1 |
<div class="pull-right"> |
2 |
<img id="imgUpload" style="width: 140px; height: 140px;" class="img-thumbnail"> |
3 |
</div>
|
Trong callback success khi tải lên tập tin, hãy cập nhật src
của #imgUpload
thành tấm ảnh đã tải lên.
1 |
$('#imgUpload').attr('src','static/Uploads/'+response.filename); |
Lưu các thay đổi ở trên và khởi động lại máy chủ. Đăng nhập vào ứng dụng và thử tải lên một tập tin hình ảnh mới và bạn sẽ có thể thấy hình ảnh đã tải lên.



Chúng ta sẽ cần sửa đổi cấu trúc bảng tbl_wish
của chúng ta để thêm ba trường mới. Thay đổi tbl_wish
như hình dưới đây:
1 |
ALTER TABLE `BucketList`.`tbl_wish` |
2 |
ADD COLUMN `wish_file_path` VARCHAR(200) NULL AFTER `wish_date`, |
3 |
ADD COLUMN `wish_accomplished` INT NULL DEFAULT 0 AFTER `wish_file_path`, |
4 |
ADD COLUMN `wish_private` INT NULL DEFAULT 0 AFTER `wish_accomplished`; |
Tiếp theo chúng ta hãy sửa đổi các thủ tục lưu trữ sp_addWish
và sp_updateWish
để bao gồm các trường mới được bổ sung vào cơ sở dữ liệu.
Sửa đổi thủ tục lưu trữ sp_addWish
để bao gồm ba trường mới được thêm vào.
1 |
USE `BucketList`; |
2 |
DROP procedure IF EXISTS `sp_addWish`; |
3 |
|
4 |
DELIMITER $$ |
5 |
USE `BucketList`$$ |
6 |
CREATE DEFINER=`root`@`localhost` PROCEDURE `sp_addWish`( |
7 |
IN p_title varchar(45), |
8 |
IN p_description varchar(1000), |
9 |
IN p_user_id bigint, |
10 |
IN p_file_path varchar(200), |
11 |
IN p_is_private int, |
12 |
IN p_is_done int |
13 |
)
|
14 |
BEGIN
|
15 |
insert into tbl_wish( |
16 |
wish_title, |
17 |
wish_description, |
18 |
wish_user_id, |
19 |
wish_date, |
20 |
wish_file_path, |
21 |
wish_private, |
22 |
wish_accomplished
|
23 |
)
|
24 |
values
|
25 |
(
|
26 |
p_title, |
27 |
p_description, |
28 |
p_user_id, |
29 |
NOW(), |
30 |
p_file_path, |
31 |
p_is_private, |
32 |
p_is_done
|
33 |
);
|
34 |
END$$ |
35 |
|
36 |
DELIMITER ; |
37 |
Đồng thời sửa đổi thủ tục lưu trữ sp_updateWish
để bao gồm ba trường vừa mới được thêm vào.
1 |
USE `BucketList`; |
2 |
DROP procedure IF EXISTS `sp_updateWish`; |
3 |
|
4 |
DELIMITER $$ |
5 |
USE `BucketList`$$ |
6 |
CREATE DEFINER=`root`@`localhost` PROCEDURE `sp_updateWish`( |
7 |
IN p_title varchar(45), |
8 |
IN p_description varchar(1000), |
9 |
IN p_wish_id bigint, |
10 |
In p_user_id bigint, |
11 |
IN p_file_path varchar(200), |
12 |
IN p_is_private int, |
13 |
IN p_is_done int |
14 |
)
|
15 |
BEGIN
|
16 |
update tbl_wish set |
17 |
wish_title = p_title, |
18 |
wish_description = p_description, |
19 |
wish_file_path = p_file_path, |
20 |
wish_private = p_is_private, |
21 |
wish_accomplished = p_is_done |
22 |
where wish_id = p_wish_id and wish_user_id = p_user_id; |
23 |
END$$ |
24 |
|
25 |
DELIMITER ; |
26 |
Tiếp theo, hãy sửa đổi phương thức của trình xử lý yêu cầu /addWish
để đọc các trường mới được post và truyền chúng vào thủ tục lưu trữ.
1 |
if request.form.get('filePath') is None: |
2 |
_filePath = '' |
3 |
else: |
4 |
_filePath = request.form.get('filePath') |
5 |
|
6 |
if request.form.get('private') is None: |
7 |
_private = 0 |
8 |
else: |
9 |
_private = 1 |
10 |
|
11 |
if request.form.get('done') is None: |
12 |
_done = 0 |
13 |
else: |
14 |
_done = 1 |
Một khi các giá trị đã được đọc, chúng ta sẽ truyền chúng vào thủ tục lưu trữ MySQL.
1 |
cursor.callproc('sp_addWish',(_title,_description,_user,_filePath,_private,_done)) |
Trong trang addWish.html
, chúng ta sẽ cần thiết lập thuộc tính name
cho các phần tử được post. Do đó, hãy thêm name
cho cả hai hộp kiểm mới được thêm vào.
1 |
<input name="private" type="checkbox"> Mark as Private <span class="glyphicon glyphicon-lock" aria-hidden="true"></span> |
2 |
|
3 |
<input name="done" type="checkbox"> Mark as Done <span class="glyphicon glyphicon-ok" aria-hidden="true"></span> |
Bây giờ chúng ta cũng cần truyền vào đường dẫn tập tin tải lên. Vì vậy, chúng ta sẽ tạo ra một input ẩn và thiết lập giá trị của nó trong hàm callback success khi tải lên tập tin.
1 |
<input type="hidden" name="filePath" id="filePath"></input> |
Thiết lập giá trị của nó trong hàm callback success khi tải lên tập tin.
1 |
success: function(response, status) { |
2 |
|
3 |
var filePath = 'static/Uploads/' + response.filename; |
4 |
$('#imgUpload').attr('src', filePath); |
5 |
|
6 |
$('#filePath').val(filePath); |
7 |
|
8 |
}
|
Lưu 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à thử thêm một mong ước mới với tất cả các chi tiết theo yêu cầu. Sau khi thêm thành công, nó sẽ được liệt kê trên trang chủ của người dùng.
Chỉnh sửa Cài đặt Sửa đổi Mong ước
Trước tiên, chúng ta cần thêm một số code HTML cho ba trường mới. Vì vậy, hãy mở userHome.html
và thêm code HTML sau đây sau title
và description
.
1 |
<div class="form-group"> |
2 |
<label for="txtPost">Photos</label> |
3 |
|
4 |
<div class="input-group"> |
5 |
<span class="input-group-btn"> |
6 |
<span class="btn btn-primary btn-file"> |
7 |
Browse… <input type="file" id="fileupload" name="file" multiple> |
8 |
</span>
|
9 |
</span>
|
10 |
<div class="pull-right"> |
11 |
<img id="imgUpload" style="width: 140px; height: 140px;" class="img-thumbnail"> |
12 |
<input type="hidden" name="filePath" id="filePath"></input> |
13 |
</div>
|
14 |
</div>
|
15 |
|
16 |
</div>
|
17 |
|
18 |
<div class="form-group"> |
19 |
<label>Mark this as private and not visible to others.</label> |
20 |
<br/>
|
21 |
<input id="chkPrivate" name="private" type="checkbox"> Mark as Private <span class="glyphicon glyphicon-lock" aria-hidden="true"></span> |
22 |
</div>
|
23 |
|
24 |
<div class="form-group"> |
25 |
<label>Have you already accomplished this?</label> |
26 |
<br/>
|
27 |
<input id="chkDone" name="done" type="checkbox"> Mark as Done <span class="glyphicon glyphicon-ok" aria-hidden="true"></span> |
28 |
</div>
|
Chúng tôi sẽ cần phải tìm nạp dữ liệu cần thiết để điền vào các trường trên khi chỉnh sửa. Do đó, chúng ta hãy sửa thủ tục lưu trữ sp_GetWishById
để bao gồm các trường bổ sung như sau:
1 |
CREATE DEFINER=`root`@`localhost` PROCEDURE `sp_GetWishById`( |
2 |
IN p_wish_id bigint, |
3 |
In p_user_id bigint |
4 |
)
|
5 |
BEGIN
|
6 |
select wish_id,wish_title,wish_description,wish_file_path,wish_private,wish_accomplished from tbl_wish where wish_id = p_wish_id and wish_user_id = p_user_id; |
7 |
END
|
Tiếp theo, chúng ta sẽ cần sửa đổi chuỗi JSON
trong phương thức của tuyến /getWishById
để bao gồm các trường mới. Sửa đổi danh sách mong ước trong /getWishById
như sau:
1 |
wish.append({'Id':result[0][0],'Title':result[0][1],'Description':result[0][2],'FilePath':result[0][3],'Private':result[0][4],'Done':result[0][5]}) |
Để kết xuất kết quả, chúng ta cần phải phân tích dữ liệu nhận được trong callback success của hàm Edit
trong JavaScript trong userHome.html
.
1 |
success: function(res) { |
2 |
|
3 |
var data = JSON.parse(res); |
4 |
|
5 |
$('#editTitle').val(data[0]['Title']); |
6 |
|
7 |
$('#editDescription').val(data[0]['Description']); |
8 |
|
9 |
$('#imgUpload').attr('src', data[0]['FilePath']); |
10 |
|
11 |
if (data[0]['Private'] == "1") { |
12 |
$('#chkPrivate').attr('checked', 'checked'); |
13 |
}
|
14 |
|
15 |
if (data[0]['Done'] == "1") { |
16 |
$('#chkDone').attr('checked', 'checked'); |
17 |
}
|
18 |
|
19 |
$('#editModal').modal(); |
20 |
|
21 |
}
|
Lưu các thay đổi 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à khi ở trang chủ của người dùng, hãy thử chỉnh sửa mong ước từ danh sách mong ước. Bạn sẽ thấy dữ liệu được nhập trong cửa sổ Chỉnh sửa bật lên.



Bây giờ, tương tự như những gì chúng ta đã làm trên trang add wish, hãy thêm tham chiếu đến script jQuery-File-Upload trong userHome.html
.
1 |
<script src="../static/js/jquery-1.11.2.js"></script> |
2 |
|
3 |
<script src="../static/js/jquery.ui.widget.js"></script> |
4 |
|
5 |
<script type="text/javascript" src="../static/js/jquery.fileupload.js"></script> |
6 |
|
7 |
<script type="text/javascript" src="../static/js/jquery.fileupload-process.js"></script> |
8 |
|
9 |
<script type="text/javascript" src="../static/js/jquery.fileupload-ui.js"></script> |
Khởi tạo control tải lên trong hộp thoại chỉnh sửa sử dụng code tương tự như chúng ta đã sử dụng trên trang add wish.
1 |
$(function() { |
2 |
$('#fileupload').fileupload({ |
3 |
url: 'upload', |
4 |
dataType: 'json', |
5 |
add: function(e, data) { |
6 |
data.submit(); |
7 |
},
|
8 |
success: function(response, status) { |
9 |
|
10 |
var filePath = 'static/Uploads/' + response.filename; |
11 |
$('#imgUpload').attr('src', filePath); |
12 |
$('#filePath').val(filePath); |
13 |
|
14 |
},
|
15 |
error: function(error) { |
16 |
console.log(error); |
17 |
}
|
18 |
});
|
19 |
})
|
Tiếp theo, chúng ta cần phải sửa đổi button Update trong hộp thoại Chỉnh sửa để bao gồm các trường bổ sung được thêm vào. Vì vậy, trong sự kiện button click btnUpdate
, hãy sửa đổi các tham số dữ liệu được truyền vào để bao gồm ba trường mới như sau:
1 |
data : {title:$('#editTitle').val(),description:$('#editDescription').val(),id:localStorage.getItem('editId'),filePath:$('#imgUpload').attr('src'),isPrivate:$('#chkPrivate').is(':checked')?1:0,isDone:$('#chkDone').is(':checked')?1:0} |
Mở app.py
và sửa đổi phương thức của trình xử lý yêu cầu /updateWish
để phân tích các trường mới được thêm vào.
1 |
_filePath = request.form['filePath'] |
2 |
_isPrivate = request.form['isPrivate'] |
3 |
_isDone = request.form['isDone'] |
Sửa đổi phương thức gọi thủ tục để bao gồm các tham số bổ sung.
1 |
cursor.callproc('sp_updateWish',(_title,_description,_wish_id,_user,_filePath,_isPrivate,_isDone)) |
Bây giờ mở sp_updateWish
và sửa đổi nó để bao gồm các trường vừa được thêm vào.
1 |
DELIMITER $$ |
2 |
|
3 |
CREATE DEFINER=`root`@`localhost` PROCEDURE `sp_updateWish`( |
4 |
IN p_title varchar(45), |
5 |
IN p_description varchar(1000), |
6 |
IN p_wish_id bigint, |
7 |
In p_user_id bigint, |
8 |
IN p_file_path varchar(200), |
9 |
IN p_is_private int, |
10 |
IN p_is_done int |
11 |
)
|
12 |
BEGIN
|
13 |
update tbl_wish set |
14 |
wish_title = p_title, |
15 |
wish_description = p_description, |
16 |
wish_file_path = p_file_path, |
17 |
wish_private = p_is_private, |
18 |
wish_accomplished = p_is_done |
19 |
where wish_id = p_wish_id and wish_user_id = p_user_id; |
20 |
END
|
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ệ, thử chỉnh sửa và cập nhật các mục hiện có.
Phần tóm tắt
Trong phần này, chúng ta đã học được cách tích hợp và sử dụng plugin jQuery-File-Upload của blueimp để tải lên hình ảnh trong ứng dụng Python Flask của chúng ta. Trong phần tiếp theo của loạt bài này, chúng ta sẽ hiển thị những mong ước đã được tạo ra bởi người dùng trên trang chủ của ứng dụng và thêm chức năng để thích những mong ước.
Hãy cho chúng tôi biết những suy nghĩ, hiệu chỉnh và đề xuất của bạn trong phần bình luận bên dưới nhé. Mã nguồn từ hướng dẫn này có sẵn trên GitHub.