Advertisement
  1. Code
  2. Python

Stwórz od zera aplikację sieciową w Python Flask i MySQL: Część 6

Scroll to top
Read Time: 10 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 5
Creating a Web App From Scratch Using Python Flask and MySQL: Part 7

() translation by (you can also view the original English article)

W poprzedniej części cyklu zaimplementowaliśmy paginację listy marzeń na stronie użytkownika. W tym odcinku wprowadzimy możliwość wstawiania obrazka odpowiadającego życzeniu, opcję oznaczenia życzenia spełnionego oraz możliwość ustawienia prywatności.

Pierwsze kroki

Zaczynamy od sklonowania poprzedniej części tutorialu z GitHub.

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

Gdy plik źródłowy zostanie sklonowany, przejdź do katalogu projektu i uruchom serwer.

1
cd PythonFlaskMySQLApp_Part5
2
python app.py

Skieruj przeglądarkę na adres http://localhost:5002/ - aplikacja powinna zadziałać.

Zmienianie interfejsu użytkownika

Na początek zajmiemy się stroną "dodaj marzenie" i wprowadzimy na niej opcję przesyłania obrazów. Przejdź do templates/addWish.html. Formularz przy addWish.html wygląda bardzo skromnie, zmieńmy więc nasz kod bootstrap HTML, by formularz wyświetlany był pionowo.

Najpierw zmienimy form-horizontal na pionową - usuń klasę form-horizontal z formularza. Dodamy też trzy nowe kontrolery: kontroler przesyłania plików, okienko do zaznaczania, czy marzenie jest prywatne i dodatkowe do odhaczenia, gdy marzenie zostanie spełnione. Tak wygląda zmodyfikowany addWish.html.

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&hellip; <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>&copy; Company 2015</p>
118
        </footer>
119
120
    </div>
121
</body>
122
123
</html>

Zapisz zmianę i zrestartuj serwer. Po zalogowaniu, kliknij w link Add Wish, by zobaczyć zmienioną stronę dodawania marzenia.

Add Wish Page with Image UploadAdd Wish Page with Image UploadAdd Wish Page with Image Upload

Implementacja funkcji przesyłania

Do zaimplementowania funkcji przesyłania plików skorzystamy z blueimp jQuery-File-Upload. Ściągnij potrzebne pliki z GitHub. Rozpakuj pliki źródłowe i dopisz następujące odwołania do 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>

Dopisz kod inicjalizacji wtyczki do przycisku dodawania plików przy załadowaniu strony addWish.html.

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

Jak widać, przypięliśmy wtyczkę do przesyłania plików do przycisku #fileupload. Wtyczka do przesyłania plików wysyła plik do procedury obsługi zapytań /upload, którą zdefiniujemy w pliku Pythona. Zdefiniowaliśmy również funkcję add do podawania danych, a także informacje zwrotne (callback) success  i failure, które obsłużą odpowiednio udaną i nieudaną próbę przesłania pliku.

Teraz zdefiniujmy funkcję obsługi przesyłania plików upload wewnątrz app.py. Zdefiniiuj przkierowanie /upload, jak w przykładzie:

1
@app.route('/upload', methods=['GET', 'POST'])
2
def upload():
3
    # file upload handler code will be here

Sprawdź, czy zapytanie jest typu POST - w takim przypadku odczytamy plik z zapytania.

1
if request.method == 'POST':
2
        file = request.files['file']

Musimy też poznać rozszerzenie pliku przesłanego obrazka, zanim go zapiszemy. Zaimportuj zatem os, a następnie wyodrębnij rozszerzenie z nazwy pliku.

1
extension = os.path.splitext(file.filename)[1]

Gdy już znamy rozszerzenie, nadajemy obrazkowi nową, unikalną nazwę za pomocą metody uuid. Zaimportuj uuid i wygeneruj nową nazwę pliku.

1
f_name = str(uuid.uuid4()) + extension

Utwórz katalog o nazwie Uploads w folderze static. Tu będziemy przechowywać przesłane pliki. Dopisz ścieżkę folderu Upload w ustawieniach aplikacji.

1
app.config['UPLOAD_FOLDER'] = 'static/Uploads'

Teraz zapisz przesłany plik pod UPLOAD_FOLDER i zwróć w odpowiedzi nazwę pliku.

1
file.save(os.path.join(app.config['UPLOAD_FOLDER'], f_name))
2
return json.dumps({'filename':f_name})

Zapisz wszystkie zmiany i uruchom serwer ponownie. Skieruj przeglądarkę na http://localhost:5002 i zaloguj się poprawnymi danymi. Spróbuj wczytać zdjęcie korzystając z przycisku browse, a następnie sprawdź, co dzieje się w konsoli przeglądarki. Powinna się pokazać nazwa wczytanego pliku.

W miejsce pola tekstu wejściowego tylko do odczytu  wstawimy element obrazka, w którym wyświetli się wczytane zdjęcie. Zastąp więc pole tekstowe następującym kodem HTML.

1
<div class="pull-right">
2
    <img id="imgUpload" style="width: 140px; height: 140px;" class="img-thumbnail">
3
</div>

W informacji zwrotnej o wczytaniu pliku zaktualizuj adres wysłanego obrazka - src przy #imgUpload

1
$('#imgUpload').attr('src','static/Uploads/'+response.filename);

Zapisz powyższe zmiany i uruchom ponownie serwer. Zaloguj się do aplikacji i spróbuj przesłać nowy plik obrazu. Powinieneś już zobaczyć przesłane zdjęcie.

Add Wish Page With UploadAdd Wish Page With UploadAdd Wish Page With Upload

Musimy teraz zmienić strukturę naszej tbl_wish - dodać do niej trzy nowe pola. Wprowadź zmiany w tbl_wish, jak pokazano poniżej:

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

Teraz zmodyfikujmy nasze procedury składowane sp_addWish i sp_updateWish, by pojawiły się w nich nowododane pola bazy.

Zmień sp_addWish, dodając trzy nowe pola.

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

Zmień również sp_updateWish, dołączając trzy nowo dodane pola.

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

A teraz zmodyfikuj funkcję obsługi zapytań /addWish, aby czytała nowoprzesłane pola i przekazywała je do procedury składowanej.

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

Po zczytaniu wartości, prześlemy je do odwołania do procedury składowanej MySQL.

1
cursor.callproc('sp_addWish',(_title,_description,_user,_filePath,_private,_done))

Na stronie addWish.html musimy ustalić atrybut name elementów, które zostaną przesłane. Dodaj zatem name przy obu nowoutworzonych polach do zaznaczania.

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>

Teraz musimy także przesłać adres do wczytanych plików. Utworzymy zatem ukryte pole wejściowe i ustalimy jego wartość w odwołaniu zwrotnym o powodzeniu przesyłania pliku.

1
<input type="hidden" name="filePath" id="filePath"></input>

Ustaw zawartość pola tekstowego w odwołaniu zwrotnym. 

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
}

Zapisz powyższe zmiany i uruchom ponownie serwer. Zaloguj się przy użyciu prawidłowych danych i spróbuj dodać nowe marzenie wraz ze wszystkimi szczegółami.. Po wprowadzeniu wszystkich danych, powinny się one pojawić na stronie domowej użytkownika.

Zmiany w implementacji Edycji Marzenia

Najpierw musimy wstawić nieco kodu HTML dla nowych trzech pól. Otwórz więc userHome.html i wklej poniższy kod HTML poniżej title i 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&hellip; <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>

Musimy odebrać wymagane dane, aby wprowadzić je w do pól formularza edycji. Zmieńmy więc procedurę składowaną sp_GetWishById, aby uwzględniała dodatkowe pola, jak pokazano:

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

Teraz musimy zmodyfikować ciąg JSON w przekierowaniu /getWishById, by uwzględniał nowe pola: Zmień listę marzeń w  /getWishById, zgodnie z instrukcją:

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]})

Aby wyrenderować stronę, musimy zczytać dane otrzymane w pozytywnej informacji zwrotnej  z funkcji JavaScript Edit na 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
}

Zapisz zmiany i uruchom serwer ponownie. Zaloguj się właściwymi danymi, a gdy znajdziesz się na stronie domowej, spróbuj edytować jedno z marzeń z listy. Dane powinny wyświetlić się w polach okienka Edit.

Edit Pop Up With Additional FieldsEdit Pop Up With Additional FieldsEdit Pop Up With Additional Fields

Teraz, w taki sam sposób, jak to zrobiliśmy na stronie dodawania marzeń, wstaw odwołanie do jQuery-File-Upload na 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>

Wstaw moduł przesyłania plików do okienka edycji korzystając z tego samego kodu, którego użyliśmy na stronie dodawania marzeń.

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

Teraz musimy zmienić reakcję na kliknięcie przycisku Update w okienku Edit, aby uwzględnić kilka nowych pól. A więc, przy kliknięciu pod btnUpdate, zmień parametry przesyłanych danych, dopisując trzy nowe pola, zgodnie z instrukcją:

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}

Otwórz app.py i zmień funkcję obsługi odwołania /updateWish tak, aby zczytywała dane z nowych pól.

1
_filePath = request.form['filePath']
2
_isPrivate = request.form['isPrivate']
3
_isDone = request.form['isDone']

Zmodyfikuj metodę wywołania procedury, wstawiając dodatkowe parametry.

1
cursor.callproc('sp_updateWish',(_title,_description,_wish_id,_user,_filePath,_isPrivate,_isDone))

Teraz otwórz sp_updateWish i zmień ją, by uwzględnić nowe pola.

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

Zapisz wszystkie zmiany i ponownie uruchom serwer. Zaloguj się prawidłowymi danymi i spróbuj edytować i aktualizować wcześniejsze wpisy.

Podsumowując

W tej części tutorialu nauczyliśmy się wstawiać i wtyczkę blueimp jQuery-File-Upload i korzystać z niej do wczytywania obrazków w aplikacji Python Flask. W kolejnej części cyklu pokażemy marzenia zrealizowane przez użytkowników na głównej stronie aplikacji i dodamy funkcję. która pozwoli je polubić.

Podziel się z nami swoją opinią, uwagami i sugestiami w komentarzach poniżej. Kod źródłowy z tego tutorialu jest dostępny na GitHub.

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.