7.1. Quiz

Realizacja aplikacji internetowej Quiz w oparciu o framework Flask 0.12.x. 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 aplikacji o nazwie quiz:

Terminal nr
~$ mkdir quiz; cd quiz;

Utworzymy szkielet aplikacji Flask, co pozwoli na uruchomienie testowego serwera www, umożliwiającego wygodne rozwijanie kodu. W nowym pliku o nazwie quiz.py wpisujemy poniższy kod i zapisujemy w katalogu aplikacji.

Kod nr
1# -*- coding: utf-8 -*-
2# quiz/quiz.py
3
4from flask import Flask
5
6app = Flask(__name__)
7
8if __name__ == '__main__':
9    app.run(debug=True)

Serwer uruchamiamy komendą:

Terminal nr
~/quiz$ python3 quiz.py
../../_images/serwer.jpg

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.

../../_images/quiz1.png

Wskazówka

Działanie serwera w terminalu zatrzymujemy skrótem CTRL+C.

7.1.2. 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 treści, np. wyniki quizu. Flask ułatwia tworzenie takich dokumentów za pomocą szablonów.

W pliku quiz.py umieszczamy kod:

Kod nr
 1# -*- coding: utf-8 -*-
 2# quiz/quiz.py
 3
 4from flask import Flask
 5
 6app = Flask(__name__)
 7
 8
 9@app.route('/')
10def index():
11    return 'Cześć, tu Python!'
12
13
14if __name__ == '__main__':
15    app.run(debug=True)

Widok (czyli funkcja) index() powiązany jest z adresem głównym (/) za pomocą dekoratora @app.route('/'). Funkcja zostanie wykonana w odpowiedzi na żądanie GET wysłane przez przeglądarkę po wpisaniu i zatwierdzeniu przez użytkownika adresu serwera.

Najprostszą odpowiedzią jest zwrócenie jakiegoś tekstu: return 'Cześć, tu Python!'.

../../_images/quiz2.png

Zazwyczaj będziemy prezentować bardziej skomplikowane dane, w dodatku sformatowane wizualnie. Potrzebujemy szablonów. Będziemy je zapisywać w katalogu quiz/templates, który utworzymy np. poleceniem:

Terminal nr
~/quiz$ mkdir templates

Następnie w nowym pliku templates/index.html umieszczamy kod:

Plik index.html. Kod nr
1<!-- quiz/templates/index.html -->
2<html>
3  <head>
4    <title>Quiz Python</title>
5  </head>
6<body>
7  <h1>Quiz Python</h1>
8</body>
9</html>

Na koniec modyfikujemy funkcję index() w pliku quiz.py:

Kod nr
 1# -*- coding: utf-8 -*-
 2# quiz/quiz.py
 3
 4from flask import Flask
 5from flask import render_template
 6
 7app = Flask(__name__)
 8
 9
10@app.route('/')
11def index():
12    # return 'Cześć, tu Python!'
13    return render_template('index.html')
14
15
16if __name__ == '__main__':
17    app.run(debug=True)

Do renderowania szablonu (zob: renderowanie szablonu) używamy funkcji render_template('index.html'), która jako argument przyjmuje nazwę pliku szablonu. Pod adresem http://127.0.0.1:5000 strony głównej, zobaczymy dokument HTML:

../../_images/quiz3.png

7.1.3. Pytania i odpowiedzi

Dane aplikacji, a więc pytania i odpowiedzi, umieścimy w liście DANE 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():

Kod nr
 1# -*- coding: utf-8 -*-
 2# quiz/quiz.py
 3
 4from flask import Flask
 5from flask import render_template
 6
 7app = Flask(__name__)
 8
 9# konfiguracja aplikacji
10app.config.update(dict(
11    SECRET_KEY='bradzosekretnawartosc',
12))
13
14# lista pytań
15DANE = [{
16    'pytanie': 'Stolica Hiszpani, to:',  # pytanie
17    'odpowiedzi': ['Madryt', 'Warszawa', 'Barcelona'],  # możliwe odpowiedzi
18    'odpok': 'Madryt'},  # poprawna odpowiedź
19    {
20    'pytanie': 'Objętość sześcianu o boku 6 cm, wynosi:',
21    'odpowiedzi': ['36', '216', '18'],
22    'odpok': '216'},
23    {
24    'pytanie': 'Symbol pierwiastka Helu, to:',
25    'odpowiedzi': ['Fe', 'H', 'He'],
26    'odpok': 'He'},
27]
28
29
30@app.route('/')
31def index():
32    # return 'Cześć, tu Python!'
33    return render_template('index.html', pytania=DANE)
34
35
36if __name__ == '__main__':
37    app.run(debug=True)

W konfiguracji aplikacji dodaliśmy sekretny klucz, wykorzystywany podczas korzystania z sesji (zob sesja).

Dane aplikacji

Każda aplikacja korzysta z jakiegoś źródła danych. W najprostszym przypadku dane zawarte są w samej aplikacji. Dodaliśmy więc listę słowników DANE, którą przekazujemy dalej jako drugi argument do funkcji render_template(). Dzięki temu będziemy mogli odczytać je w szablonie w zmiennej pytania.

Do szablonu index.html wstawiamy poniższy kod po nagłówku <h1>.

Plik index.html. Kod nr
 1    <!-- formularz z quizem -->
 2    <form method="POST">
 3        <!-- przeglądamy listę pytań -->
 4        {% for p in pytania %}
 5          <p>
 6            <!-- wyświetlamy treść pytania -->
 7            {{ p.pytanie }}
 8            <br>
 9            <!-- zapamiętujemy numer pytania licząc od zera -->
10            {% set pnr = loop.index0 %}
11            <!-- przeglądamy odpowiedzi dla danego pytania -->
12            {% for o in p.odpowiedzi %}
13              <label>
14                <!-- odpowiedzi wyświetlamy jako pole typu radio -->
15                <input type="radio" value="{{ o }}" name="{{ pnr }}">
16                {{ o }}
17              </label>
18              <br>
19            {% endfor %}
20          </p>
21        {% endfor %}
22
23        <!-- przycisk wysyłający wypełniony formularz -->
24        <button type="submit">Sprawdź odpowiedzi</button>
25    </form>

Znaczniki HTML w powyższym kodzie tworzą formularz (<form>). Natomiast tagi, czyli polecenia dostępne w szablonach, pozwalają wypełnić go danymi.

  • {% instrukcja %} – tak wstawiamy instrukcje sterujące;

  • {{ zmienna }} – tak wstawiamy wartości zmiennych przekazanych do szablonu.

Z przekazanej 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ą:

../../_images/quiz4.png

7.1.4. Oceniamy odpowiedzi

Mechanizm sprawdzana liczby poprawnych odpowiedzi umieścimy w funkcji index(). Na początku pliku quiz.py dodajemy potrzebne importy:

Kod nr
6from flask import request, redirect, url_for, flash

– i uzupełniamy kod funkcji index():

Kod nr
30@app.route('/', methods=['GET', 'POST'])
31def index():
32
33    if request.method == 'POST':
34        punkty = 0
35        odpowiedzi = request.form
36
37        for pnr, odp in odpowiedzi.items():
38            if odp == DANE[int(pnr)]['odpok']:
39                punkty += 1
40
41        flash('Liczba poprawnych odpowiedzi, to: {0}'.format(punkty))
42        return redirect(url_for('index'))
43
44    # return 'Cześć, tu Python!'
45    return render_template('index.html', pytania=DANE)
  • methods=['GET', 'POST'] – lista zawiera obsługiwane typy żądań, chcemy obsługiwać zarówno żądania GET (odesłanie żądanej strony), jak i POST (ocena przesłanych odpowiedzi i odesłanie wyniku);

  • if request.method == 'POST': – instrukcja warunkowa, która wykrywa żądania POST i wykonuje blok kodu zliczający poprawne odpowiedzi;

  • odpowiedzi = request.form – przesyłane dane z formularza pobieramy z obiektu request i zapisujemy w zmiennej odpowiedzi;

  • for pnr, odp in odpowiedzi.items() – w pętli odczytujemy kolejne pary danych, czyli numer pytania i udzieloną odpowiedź;

  • if odp == DANE[int(pnr)]['odpok']: – sprawdzamy, czy nadesłana odpowiedź jest zgodna z poprawną, którą wydobywamy z listy pytań.

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:

Plik index.html. Kod nr
 9    <!-- wyświetlamy komunikaty z funkcji flash -->
10    <p>
11      {% for message in get_flashed_messages() %}
12        {{ message }}
13      {% endfor %}
14    </p>

Po uruchomieniu aplikacji, zaznaczeniu odpowiedzi i ich przesłaniu otrzymujemy ocenę.

../../_images/quiz5.png

7.1.5. Materiały

Źródła:

  • Instrukcja w pdf dla Pythona 2


Licencja Creative Commons 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:

2025-04-12 o 10:21 w Sphinx 7.3.7

Autorzy:

Patrz plik „Autorzy”