7.1. Quiz¶
Realizacja aplikacji internetowej Quiz w oparciu o framework Flask. Na stronie wyświetlamy pytania, użytkownik zaznacza poprawne odpowiedzi, przesyła je na serwer i otrzymuje informację o wynikach.
7.1.1. Projekt i aplikacja¶
W katalogu użytkownika tworzymy nowy katalog dla aplikacji quiz
,
a w nim plik główny quiz.py
:
~$ mkdir quiz; cd quiz; touch quiz.py
Utworzymy szkielet aplikacji Flask, co pozwoli na uruchomienie testowego serwera www,
umożliwiającego wygodne rozwijanie kodu. W pliku quiz.py
wpisujemy:
1 2 3 4 5 6 7 8 9 | # -*- coding: utf-8 -*-
# quiz/quiz.py
from flask import Flask
app = Flask(__name__)
if __name__ == '__main__':
app.run(debug=True)
|
Serwer uruchamiamy komendą:
~/quiz$ python quiz.py
Domyślnie serwer uruchamia się pod adresem http://127.0.0.1:5000. Po wpisaniu go do przeglądarki internetowej otrzymamy kod odpowiedzi HTTP 404, tj. błąd “nie znaleziono”, co wynika z faktu, że nasza aplikacja nie ma jeszcze zdefiniowanego żadnego widoku dla tego adresu.
7.1.2. Widok (strona główna)¶
Jeżeli chcemy, aby nasza aplikacja zwracała użytkownikowi jakieś strony www, tworzymy tzw. widok. Jest to funkcja Pythona powiązana z określonymi adresami URL za pomocą tzw. dekoratorów. Widoki pozwalają nam obsługiwać podstawowe żądania protokołu HTTP, czyli: GET, wysyłane przez przeglądarkę, kiedy użytkownik chce zobaczyć stronę, i POST, kiedy użytkownik przesyła dane na serwer za pomocą formularza.
W odpowiedzi aplikacja może odsyłać różne dane. Najczęściej będą to znaczniki HTML oraz żądane treści, np. wyniki quizu. Flask ułatwia tworzenie takich dokumentów za pomocą szablonów.
W pliku quiz.py
umieszczamy funkcję index()
, czyli widok strony głównej:
1 2 3 4 5 6 7 8 9 10 11 12 13 | # -*- coding: utf-8 -*-
# quiz/quiz.py
from flask import Flask
app = Flask(__name__)
@app.route('/')
def index():
return 'Cześć, tu Python!'
if __name__ == '__main__':
app.run(debug=True)
|
Widok (czyli funkcja) index()
powiązana jest z adresem głównym (/)
za pomocą dekoratora @app.route('/')
. Dzięki temu, jeżeli użytkownik
wpisze w przeglądarce adres serwera, jego żądanie (GET)
zostanie przechwycone i obsłużone właśnie w tej funkcji.
Najprostszą odpowiedzią na żądanie GET jest zwrócenie jakiegoś tekstu.
Tak też robimy wywołując funkcję return 'Cześć, tu Python!'
, która odeśle
podany tekst do przeglądarki, a ta wyświetli go użytkownikowi.
Zazwyczaj będziemy prezentować bardziej skomplikowane dane, w dodatku
sformatowane wizualnie. Potrzebujemy szablonu.
Tworzymy więc plik ~/quiz/templates/index.html
.
Można to zrobić w terminalu po ewentualnym zatrzymaniu serwera (CTRL+C):
~/quiz$ mkdir templates; touch templates/index.html
Jak widać szablony umieszczamy w podkatalogu templates
aplikacji.
Do pliku index.html
wstawiamy poniższy kod HTML:
1 2 3 4 5 6 7 8 9 | <!-- quiz/templates/index.html -->
<html>
<head>
<title>Quiz Python</title>
</head>
<body>
<h1>Quiz Python</h1>
</body>
</html>
|
Na koniec modyfikujemy funkcje index()
w pliku quiz.py:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | # -*- coding: utf-8 -*-
# quiz/quiz.py
from flask import Flask
from flask import render_template
app = Flask(__name__)
@app.route('/')
def index():
#return 'Cześć, tu Python!'
return render_template('index.html')
if __name__ == '__main__':
app.run(debug=True)
|
Po zaimportowaniu (!) potrzebnej funkcji używamy jej do wyrenderowania
podanego jako argument szablonu: return render_template('index.html')
.
Pod adresem http://127.0.0.1:5000 strony głównej, zobaczymy dokument HTML:
7.1.3. Pytania i odpowiedzi¶
Dane aplikacji, a więc pytania i odpowiedzi, umieścimy w liście
PYTANIA
w postaci słowników zawierających: treść pytania,
listę możliwych odpowiedzi oraz poprawną odpowiedź.
Modyfikujemy plik quiz.py
. Podany kod wstawiamy po inicjacji zmiennej
app
, ale przed dekoratorem widoku index()
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | # -*- coding: utf-8 -*-
# quiz/quiz.py
from flask import Flask
from flask import render_template
app = Flask(__name__)
# konfiguracja aplikacji
app.config.update(dict(
SECRET_KEY='bradzosekretnawartosc',
))
# lista pytań
PYTANIA = [
{
'pytanie': u'Stolica Hiszpani, to:',# pytanie
'odpowiedzi': [u'Madryt', u'Warszawa', u'Barcelona'], # możliwe odpowiedzi
'odpok': u'Madryt', # poprawna odpowiedź
},
{
'pytanie': u'Objętość sześcianu o boku 6 cm, wynosi:',
'odpowiedzi': [u'36', u'216', u'18'],
'odpok': u'216',
},
{
'pytanie': u'Symbol pierwiastka Helu, to:',
'odpowiedzi': [u'Fe', u'H', u'He'],
'odpok': u'He',
}
]
@app.route('/')
def index():
#return 'Cześć, tu Python!'
return render_template('index.html', pytania=PYTANIA)
if __name__ == '__main__':
app.run(debug=True)
|
Dodaliśmy konfigurację aplikacji w postaci słownika, ustalając sekretny klucz,
potrzebny do zarządzania sesjami różnych użytkowników.
Najważniejszą zmianą jest dołożenie drugiego argumentu funkcji render_template()
,
czyli słownika PYTANIA
w zmiennej pytania
. Dzięki temu będziemy
mogli odczytać je w szablonie.
Do szablonu index.html
wstawiamy poniższy kod po nagłówku <h1>
.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 | <!-- formularz z quizem -->
<form method="POST">
<!-- przeglądamy listę pytań -->
{% for p in pytania %}
<p>
<!-- wyświetlamy treść pytania -->
{{ p.pytanie }}
<br>
<!-- zapamiętujemy numer pytania licząc od zera -->
{% set pnr = loop.index0 %}
<!-- przeglądamy odpowiedzi dla danego pytania -->
{% for o in p.odpowiedzi %}
<label>
<!-- odpowiedzi wyświetlamy jako pole typu radio -->
<input type="radio" value="{{ o }}" name="{{ pnr }}">
{{ o }}
</label>
<br>
{% endfor %}
</p>
{% endfor %}
<!-- przycisk wysyłający wypełniony formularz -->
<button type="submit">Sprawdź odpowiedzi</button>
</form>
|
Znaczniki HTML w powyższym kodzie tworzą formularz (<form>
).
Natomiast tagi, czyli polececnia dostępne w szablonach, pozwalają
wypełnić go danymi. Warto zapamiętać, że jeżeli potrzebujemy w szablonie instrukcji sterującej,
umieszczamy ją w znacznikach {% %}
, natomiast kiedy chcemy
wyświetlić jakąś zmienną używamy notacji {{ }}
.
Z przekazaneej do szablonu listy pytań, czyli ze zmiennej pytania
odczytujemy
w pętli {% for p in pytania %}
kolejne słowniki; dalej tworzymy elementy formularza,
czyli wyświetlamy treść pytania {{ p.pytanie }}
,
a w kolejnej pętli {% for o in p.odpowiedz %}
odpowiedzi w postaci grupy opcji typu radio.
Każda grupa odpowiedzi nazywana jest dla odróżnienia numerem pytania liczonym od 0.
Odpowiednią zmienną ustawiamy w instrukcji {% set pnr = loop.index0 %}
,
a używamy w postaci name="{{ pnr }}"
. Dzięki temu przyporządkujemy
przesłane odpowiedzi do kolejnych pytań podczas ich sprawdzania.
Po ponownym uruchomieniu serwera powinniśmy otrzymać następującą stronę internetową:
7.1.4. Oceniamy odpowiedzi¶
Mechanizm sprawdzana liczby poprawnych odpowiedzi umieścimy
w funkcji index()
. Uzupełniamy więc plik quiz.py
:
32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 | from flask import request, redirect, url_for, flash
@app.route('/', methods=['GET', 'POST'])
def index():
if request.method == 'POST':
punkty = 0
odpowiedzi = request.form
for pnr, odp_u in odpowiedzi.items():
if odp_u == PYTANIA[int(pnr)]['odpok']:
punkty += 1
flash(u'Liczba poprawnych odpowiedzi, to: {0}'.format(punkty))
return redirect(url_for('index'))
#return 'Cześć, tu Python!'
return render_template('index.html', pytania=PYTANIA)
|
Przede wszystkim importujemy potrzebne funkcje. Następnie
uzupełniamy dekorator app.route()
, aby obsługiwał zarówno żądania GET
(odesłanie żądanej strony), jak i POST (ocena przesłanych odpowiedzi
i odesłanie wyniku).
Instrukcja warunkowa if request.method == 'POST':
wykrywa żądania POST
i wykonuje blok kodu zliczający poprawne odpowiedzi.
Dane pobieramy z przesłanego formularza i zapisujemy w zmiennej:
odpowiedzi = request.form
. Następnie w pętli
for pnr, odp_u in odpowiedzi.items()
odczytujemy
kolejne pary danych, czyli numer pytania i udzieloną odpowiedź.
Instrukcja if odp_u == PYTANIA[int(pnr)]['odpok']:
sprawdza,
czy nadesłana odpowiedź jest zgodna z poprawną, którą wydobywamy z
listy pytań za pomocą zmiennej pnr
i klucza odpok
.
Zwróćmy uwagę, że wartości zmiennej pnr
, czyli numery pytań liczone od zera,
ustaliliśmy wcześniej w szablonie.
Jeżeli nadesłana odpowiedź jest poprawna, doliczamy punkt (punkty += 1
).
Informacje o wyniku przekazujemy użytkownikowi za pomocą funkcji flash()
,
która korzysta z tzw. sesji HTTP (wykorzystującej SECRET_KEY
),
czyli mechanizmu pozwalającego na rozróżnianie żądań przychodzących
w tym samym czasie od różnych użytkowników.
W szablonie index.html
między znacznikami <h1>
i <form>
wstawiamy instrukcje wyświetlające wynik:
9 10 11 12 13 14 | <!-- wyświetlamy komunikaty z funkcji flash -->
<p>
{% for message in get_flashed_messages() %}
{{ message }}
{% endfor %}
</p>
|
Po uruchomieniu aplikacji, zaznaczeniu odpowiedzi i ich przesłaniu otrzymujemy ocenę.
7.1.5. Materiały¶
Źródła:
Kolejne wersje tworzenego kodu znajdziesz w katalogu ~/python101/docs/webapps/quiz
.
Uruchamiamy je wydając polecenia:
~/python101$ cd docs/webapps/quiz
~/python101/docs/webapps/bazy$ python quizx.py
- gdzie x jest numerem kolejnej wersji kodu.
Materiały Python 101
udostępniane przez
Centrum Edukacji Obywatelskiej na licencji
Creative Commons Uznanie autorstwa-Na tych samych warunkach 4.0 Międzynarodowa.
Utworzony: | 2017-09-08 o 19:38 w Sphinx 1.4.5 |
---|---|
Autorzy: | Patrz plik “Autorzy” |