Indonesian (Bahasa Indonesia) translation by Aditia Dwiperdana (you can also view the original English article)
Pada bagian sebelumnya dari seri tutorial ini, kita mengimplementasi pembagian halaman untuk daftar keinginan pada halaman beranda user. Pada tutorial kali ini, kita akan mengimplementasi opsi untuk user mengunggah gambar yang mewakili sebuah keinginan, sebuah opsi untuk menandai keinginan tercapai, dan opsi untuk mengatur privasi.
Memulai
Kita mulai dengan mengklon tutorial bagian sebelumnya dari GitHub.
1 |
git clone https://github.com/jay3dec/PythonFlaskMySQLApp_Part5.git |
Begitu source code di klon, masuk ke direktori proyek dan jalankan web server.
1 |
cd PythonFlaskMySQLApp_Part5
|
2 |
python app.py |
Arahkan browser ke http://localhost:5002/ dan aplikasi akan berjalan.
Mengubah Tampilan Antarmuka
Kita mulai dengan mengubah halaman 'tambahkan keinginan' dengan menambahkan opsi untuk menunggah sebuah gambar. Masuk ke templates/addWish.html
. Form pada addWish.html
terlihat kecil, mari kita ubah kode HTML bootstrap agar membuat form vertikal.
Pertama kita ubah form-horizontal
menjadi form vertikal, jadi hilangkan kelas form-horizontal
dari form. Kita tambahkan juga tiga kontrol baru: kontrol unggah file untuk mengunggah foto, ceklis untuk menandai keinginan menjadi pribadi, dan ceklis lain untuk menandai keinginan sudah tercapai. Berikut adalah addWish.html
yang sudah diubah.
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>
|
Simpan perubahan di atas dan restart server. Setelah sukses sign in, klik tautan Add Wish dan kamu bisa melihat halaman menambah keinginan yang sudah diubah.



Mengimplementasi Fungsi Mengunggah
Kita akan menggunakan blueimp jQuery-File-Upload untuk mengimplementasi fitur unggah file. Unduh file yang dibutuhkan dari GitHub. Ekstrak kode sumber dan tambahkan referensi skrip berikut ke 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> |
Saat addWish.html
dibuka, tambahkan kode inisialisasi plugin ke klik tombol unggah.
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 |
})
|
Seperti terlihat pada kode di atas, kita menambahkan plugin unggah file ke tombol #fileupload
. Plugin unggah file mengirim file ke request handler /upload
, yang akan kita definisikan dalam kode Python. Kita juga mendefinisikan sebuah fungsi add
untuk mengirim data, dan kondisi success
dan failure
untuk menangani proses unggah yang berhasil dan gagal.
Lalu kita definisikan file upload handler Python upload
dalam app.py
. Definisikan sebuah rute /upload
sebagai berikut:
1 |
@app.route('/upload', methods=['GET', 'POST']) |
2 |
def upload(): |
3 |
# file upload handler code will be here
|
Periksa apakah request adalah POST
, jika iya, baca file dari request tersebut.
1 |
if request.method == 'POST': |
2 |
file = request.files['file'] |
Kita juga perlu untuk mendapatkan ekstensi file gambar untuk menyimpan file tersebut. Import os
dan bagi nama ekstensi dari nama file.
1 |
extension = os.path.splitext(file.filename)[1] |
Setelah kita mendapat ekstensi file, kita buat nama file unik menggunakan uuid
. Import uuid
dan buat nama file.
1 |
f_name = str(uuid.uuid4()) + extension |
Buat sebuah folder bernama Uploads
pada folder static. Ini adalah tempat kita menyimpan gambar yang sudah diunggah. Tambahkan jalur ke folder Upload dalam pengaturan aplikasi.
1 |
app.config['UPLOAD_FOLDER'] = 'static/Uploads' |
Sekarang simpan file yang dikirim ke lokasi UPLOAD_FOLDER
dan kembalikan nama file sebagai respon.
1 |
file.save(os.path.join(app.config['UPLOAD_FOLDER'], f_name)) |
2 |
return json.dumps({'filename':f_name}) |
Simpan perubahan di atas dan restart server. Arahkan browser ke http://localhost:5002 dan sign in menggunakan email dan password yang sah. Cobalah upload gambar menggunakan tombol browse, dan ketika selesai, periksa konsol browsermu. Kamu akan lihat nama file yang diunggah.
Mari tambahkan elemen gambar untuk menampilkan gambar yang diunggah. Ganti input text read-only menjadi kode HTML berikut.
1 |
<div class="pull-right"> |
2 |
<img id="imgUpload" style="width: 140px; height: 140px;" class="img-thumbnail"> |
3 |
</div>
|
Pada kondisi sukses unggah file, perbarui src
#imgUpload
menjadi gambar yang diunggah.
1 |
$('#imgUpload').attr('src','static/Uploads/'+response.filename); |
Simpan perubahan di atas dan restart server. Masuk ke aplikasi dan coba unggah file gambar baru, dan kamu akan bisa melihat gambar yang diupload.



Kita perlu memodifikasi struktur tabel tbl_wish
untuk mencakup tiga kolom tambahan. Ubah tbl_wish
seperti berikut ini:
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`; |
Lalu ubah stored procedure sp_addWish
dan sp_updateWish
untuk mencakup kolom baru ke database.
Ubah stored procedure sp_addWish
agar mencakup tiga kolom baru.
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 |
Ubah pula stored procedure sp_updateWish
agar mencakup tiga kolom baru.
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 |
Lalu ubah request handler /addWish
agar membaca kolom yang baru dikirim dan melemparnya ke stored procedure.
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 |
Setelah nilai dibaca, kita lempar ke pemanggilan stored procedure MySQL.
1 |
cursor.callproc('sp_addWish',(_title,_description,_user,_filePath,_private,_done)) |
Pada halaman addWish.html
, kita perlu mengatur atribut name
untuk elemen yang akan dikirim. Tambahkan name
ke kedua ceklis yang baru ditambahkan.
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> |
Kita juga perlu mengirim jalur file yang diunggah. Kita buat input field tersembunyi dan mengatur nilainya di kondisi sukses unggah file.
1 |
<input type="hidden" name="filePath" id="filePath"></input> |
Atur nilainya pada kondisi sukses unggah file.
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 |
}
|
Simpan perubahan di atas dan restart server. Masuk menggunkan kredensial yang sah dan coba tambahkan keinginan baru dengan detail yang dibutuhkan. Setelah ditambahkan dengan sukses, butir tersebut akan mundul di daftar pada halaman beranda user.
Ubah Implementasi Edit Keinginan
Pertama kita perlu menambahkan kode HTML untuk tiga kolom baru. Buka userHome.html
dan tambahkan kode HTML berikut setelah title
dan description
HTML.
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>
|
Kita perlu mengambil beberapa data yang dibutuhkan untuk mengisi kolom-kolom di atas saat proses edit. Mari ubah stored procedure sp_GetWishById
untuk mencakup kolom tambahan sebagai berikut:
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
|
Lalu, ubah string JSON
pada rute /getWishById
untuk mencakup kolom baru. Ubah daftar keinginan pada /getWishById
sebagai berikut:
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]}) |
Untuk merender hasilnya, kita perlu mengurai data yang diterima di kondisi sukses fungsi JavaScript Edit
di 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 |
}
|
Simpan perubahan dan restart server. Masuk dengan kredensial yang sah, dan pada halaman beranda user, cobalah untuk mengedit sebuah butir keinginan dari daftar keinginan. Kamu akan mendapati data yang sudah terisi di popup Edit.



Sekarang, sama dengan yang kita lakukan pada halaman menambahkan keinginan, tambahkan referensi skrip jQuery-File-Upload di 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> |
Inisialisasi kontrol unggah file pada popup edit menggunakan kode yang sama yang kita gunakan pada halaman menambahkan keinginan.
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 |
})
|
Lalu kita perlu mengubah tombol Update pada popup Edit untuk mencakup kolom tambahan. Saat menekan tombol btnUpdate
, ubah data parameter yang dilempar untuk mencakup tiga kolom tambahan sebagai berikut:
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} |
Buka app.py
dan ubah request handler /updateWish
untuk mengurai kolom yang baru ditambahkan.
1 |
_filePath = request.form['filePath'] |
2 |
_isPrivate = request.form['isPrivate'] |
3 |
_isDone = request.form['isDone'] |
Ubah prosedur yang memanggil fungsi untuk mencakup parameter tambahan.
1 |
cursor.callproc('sp_updateWish',(_title,_description,_wish_id,_user,_filePath,_isPrivate,_isDone)) |
Buka sp_updateWish
dan ubah untuk mencakup kolom yang baru ditambahkan.
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
|
Simpan semua perubahan di atas dan restart server. Masuklah dengan kredential yang sah dan coba edit dan perbarui entri yang sudah ada.
Penutup
Pada tutorial bagian ini, kita lihat bagaimana cara mengintegrasi dan menggunakan plugin blueimp jQuery-File-Upload untuk mengunggah gambar pada aplikasi Python Flask kita. Pada bagian berikutnya, kita akan menampilkan keinginan yang sudah tercapai oleh user pada halaman utama dan menambahkan fitur untuk menyukai keinginan.
Beri tahu kami pendapat, koreksi, dan saran pada komentar di bawah. Source code tutorial ini tersedia pada GitHub.