6.1. Quiz

Realizacja aplikacji internetowej Quiz w oparciu o framework Flask 3.1.x Na stronie wyświetlamy pytania, użytkownik zaznacza poprawne odpowiedzi, przesyła je na serwer i otrzymuje informację o wynikach.

6.1.1. Środowisko pracy

Do tworzenia aplikacji z użyciem Flaska możesz użyć dowolnych narzędzi, np. terminala i ulubionego edytora kodu. Sugerujemy jednak wykorzystanie środowiska PyCharm, ponieważ w dużym stopniu ułatwia pracę nad projektami w języku Python.

Przed rozpoczęciem pracy przygotuj w katalogu projekty_flask` wirtualne środowisko Pythona i w aktywnym środowisku zainstaluj pakiet Flask:

(.venv) ~/projekty_flask$ pip install flask

6.1.2. Projekt i aplikacja

W katalogu projekty_flask` tworzymy nowy katalog aplikacji o nazwie quiz:

Terminal nr
(.venv) ~/projekty_flask$ mkdir quiz
(.venv) ~/projekty_flask$ cd quiz

Utworzymy szkielet aplikacji Flask, co pozwoli na uruchomienie testowego serwera www. W katalogu aplikacji quiz dodajemy plik o nazwie app.py i umieszczamy w nim poniższy kod.

Plik app.py Kod nr
 1from flask import Flask
 2
 3app = Flask(__name__)
 4
 5@app.route('/')
 6def index():
 7    return 'Cześć, tu Python i Flask!'
 8
 9with app.app_context():
10    if __name__ == "__main__":
11        app.run(debug=True)

Serwer uruchamiamy jednym z poleceń w terminalu w katalogu projektu: flask run --debug lub python3 app.py (py app.py w systemie Windows):

Terminal nr
(.venv) ~/projekty_flask/quiz$ flask run --debug
../../_images/flask_run_01.png

Domyślnie serwer uruchamia się pod adresem http://127.0.0.1:5000. Po otwarciu przeglądarki i wpisaniu tego adresu (można też kliknąć adres wyświetlony w terminalu z wciśniętym klawiszem CTLR) zobaczymy napis zwrócony przez aplikację: „Cześć, tu Python i Flask!”.

../../_images/flask_strona_01.png

Wskazówka

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

6.1.3. Strona główna

Nasza aplikacja zwraca użytkownikowi stronę główną za pomocą widoku index(). Jest to funkcja Pythona powiązana z określonym w dekoratorze route('/') adresem / oznaczającym domyślny zasób serwera.

Widoki obsługują podstawowe żądania protokołu HTTP wysyłane przez przeglądarkę.

Informacja

Żądania typu GET występują wtedy, kiedy chcemy pobrać jakieś dane z serwera, np. stronę. Ewentualne dane przekazywane do serwera (np. parametry wyszukiwania) widoczne są w adresie URL, np.:

www.google.com/search?client=firefox-b-lm&channel=entpr&q=żądania+GET+i+POST

Żądanie typu POST służą natomiast do przesyłania na serwer danych niewidocznych w adresie URL, które mają zazwyczaj zmieniać stan aplikacji, np. dodać lub zalogować użytkownika. Do przesyłania danych metodą POST służą formularze.

W odpowiedzi na żądanie aplikacja może odsyłać różne dane, np. predefiniowany tekst, ale najczęściej będzie to strona HTML zawierająca różne w zależności od żądania informacje, np. formularz z pytaniami albo wyniki quizu.

Flask ułatwia tworzenie takich stron za pomocą tzw. szablonów (zob. szablon). Szablony umieścimy w katalogu quiz/templates, kóry należy utworzyć:

Terminal nr
(.venv) ~/projekty_flask/quiz$ mkdir templates

Następnie w utworzonym katalogu dodajemy plik index.html i umieszczamy w nim kod:

Plik index.html. Kod nr
1<!-- projekty_flask/quiz/templates/index.html -->
2<html>
3  <head>
4    <title>{{ config.SITE_NAME }}</title>
5  </head>
6<body>
7  <h1>{{ config.SITE_NAME }}</h1>
8</body>
9</html>

W miejsce oznaczone tagiem {{ config.SITE_NAME }} wstawiona zostanie nazwa serwisu, którą zdefiniujemy w ustawieniach aplikacji.

Uzupełniamy i zmieniamy kod w pliku app.py:

Plik app.py Kod nr
 1from flask import Flask, render_template
 2
 3app = Flask(__name__)
 4
 5# konfiguracja aplikacji
 6app.config.update(dict(
 7    SECRET_KEY='bradzosekretnawartosc',
 8    SITE_NAME='Quiz Python'
 9))
10
11@app.route('/')
12def index():
13    # return 'Cześć, tu Python i Flask!'
14    return render_template('index.html')
15
16with app.app_context():
17    if __name__ == "__main__":
18        app.run(debug=True)

Konfiguracja aplikacji przechowywana jest w słowniku config, do którego dodajemy sekretny klucz, wykorzystywany do obsługi sesji (zob sesja) oraz nazwę serwisu.

Do renderowania szablonu (zob: renderowanie szablonu) używamy zaimportowanej na początku pliku funkcji render_template('index.html'), która jako argument przyjmuje nazwę pliku szablonu.

Po otworzeniu strony głównej (http://127.0.0.1:5000) zobaczymy stronę:

../../_images/flask_strona_02.png

6.1.4. Pytania i odpowiedzi

Dane aplikacji, a więc pytania i odpowiedzi, umieścimy w pliku app.py – to najprostsze rozwiązanie, żeby nie wprowadzać na razie obsługi bazy danych. Nowy kod wstawiamy po instrukcji app = Flask(__name__):

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

Źródłem danych dla naszej aplikacji będzie lista dane, która zawiera trzy słowniki. W każdym słowniku umieszczamy treść pytania, możliwe odpowiedzi oraz odpowiedź poprawną.

Dalej definiujemy nowy adres URL /pytania, który będzie obsługiwany przez funkcję pytania(). W funkcji zwracamy szablon pytania.html, do którego przekazujemy listę dane w zmiennej pytania.

W katalogu templates tworzymy nowy plik pytania.html i wstawiamy do niego poniższy kod:

Plik pytania.html Kod nr
 1<!-- projekty_flask/quiz/templates/pytania.html -->
 2<html>
 3  <head>
 4    <title>Quiz Python</title>
 5  </head>
 6  <body>
 7    <h1>Quiz Python</h1>
 8
 9    <!-- wyświetlamy komunikaty z funkcji flash -->
10    <p>
11      {% for komunikat in get_flashed_messages() %}
12        {{ komunikat }}
13      {% endfor %}
14    </p>
15
16    <!-- formularz z quizem -->
17    <form method="POST" action="{{ url_for('pytania') }}">
18        <!-- przeglądamy listę pytań -->
19        {% for p in pytania %}
20          <p>
21            <!-- wyświetlamy treść pytania -->
22            {{ p.pytanie }}
23            <br>
24            <!-- zapamiętujemy numer pytania licząc od zera -->
25            {% set pnr = loop.index0 %}
26            <!-- przeglądamy odpowiedzi dla danego pytania -->
27            {% for o in p.odpowiedzi %}
28              <label>
29                <!-- odpowiedzi wyświetlamy jako pole typu radio -->
30                <input type="radio" value="{{ o }}" name="{{ pnr }}">
31                {{ o }}
32              </label>
33              <br>
34            {% endfor %}
35          </p>
36        {% endfor %}
37
38        <!-- przycisk wysyłający wypełniony formularz -->
39        <button type="submit">Sprawdź odpowiedzi</button>
40    </form>
41
42  <p>
43    <a href="{{ url_for('index') }}">Strona główna</a>
44  </p>
45
46  </body>
47</html>

W szablonie używamy specjalnych tagów:

  • {% instrukcja %} – tak wstawiamy instrukcje sterujące typu for, set czy if;

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

Pętla {% for komunikat in get_flashed_messages() %} odpowiada za wstawienie komunikatów, które przygotujemy dla użytkownika w widoku pytania().

Znacznik <form> tworzy formularz, który wysyłany będzie na adres URL zdefiniowany w atrybucie action. Adres ten generujemy przy użyciu funkcji url_for('pytania'). Jako argument przyjmuje ona nazwę widoku.

W formularzu wstawiamy pytania i możliwe odpowiedzi: z przekazanej do szablonu zmiennej pytania odczytujemy w pętli {% for p in pytania %} kolejne słowniki. Dalej w znacznikach akapitu <p> wyświetlamy treść pytania {{ p.pytanie }}, a w kolejnej pętli {% for o in p.odpowiedz %} – odpowiedzi jako grupy opcji typu radio <input type="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.

Na końcu w znaczniku <a> generujemy adres strony głównej za pomocą wspomnianej funkcji url_for('index'), której argumentem jest nazwa widoku, czyli funkcji w tym przypadku obsługującej adres domyślny.

Po uruchomieniu serwera i wpisaniu w przeglądarce adresu http://127.0.0.1:5000/pytania powinniśmy zobaczyć:

../../_images/flask_pytania_01.png

6.1.5. Oceniamy odpowiedzi

Kod sprawdzający liczbę poprawnych odpowiedzi umieścimy w widoku pytania(). Na początku pliku app.py dodajemy potrzebne importy:

Plik app.py Kod nr
3from flask import request, redirect, url_for, flash

– i uzupełniamy kod funkcji pytania():

Plik app.py Kod nr
27@app.route('/pytania', methods=['GET', 'POST'])
28def pytania():
29    if request.method == 'POST':
30        punkty = 0
31        odpowiedzi = request.form
32
33        for pnr, odp in odpowiedzi.items():
34            if odp == dane[int(pnr)]['odpok']:
35                punkty += 1
36
37        flash('Liczba poprawnych odpowiedzi, to: {0}'.format(punkty))
38        return redirect(url_for('pytania'))
39    return render_template('pytania.html', pytania=dane)
  • methods=['GET', 'POST'] – dodajemy typy żądań, które chcemy obsługiwać, tj. GET (odesłanie zażądanej strony), jak i POST (przesłanie danych z formularza, tj. odpowiedzi, i odesłanie wyników);

  • request – obiekt zawierający informacje związane z żądaniem, m. in. typ żądania i ewentualne dane z formularza;

  • 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 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ą odczytujemy 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 (if odp == dane[int(pnr)]['odpok']:), tzn. zgadza się z poprawną odpowiedzią zawartą w liście słowników – doliczamy punkt (punkty += 1).

Informacje o wyniku przekazujemy użytkownikowi za pomocą funkcji flash(). W szablonie wyświetlamy je we wspominanej pętli {% for komunikat in get_flashed_messages() %}.

6.1.5.1. Ćwiczenie

  1. Wstaw w szablonie strony głównej link do strony wyświetlającej pytania.

    ../../_images/flask_strona_03.png
  2. Uruchom serwer, wejdź na stronę główną, a następnie na stronę z pytaniami.

    ../../_images/flask_pytania_01.png
  3. Zaznacz odpowiedzi i prześlij dane na serwer.

    ../../_images/flask_pytania_02.png

6.1.6. Materiały


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:

2026-05-30 o 19:12 w Sphinx 7.3.7

Autorzy:

Robert Bednarz