Flask — podstawy, routing, formularze, szablony Jinja2, prosty CRUD.
Flask to mikro-framework webowy dla Pythona — lekki, prosty i wystarczający do zadań egzaminacyjnych. Pozwala budować aplikacje webowe w czystym Pythonie.
pip install flaskhttp://127.0.0.1:5000debug=True — automatyczne przeładowanie po zmianachstatic/app.py — główny plik aplikacjitemplates/ — pliki HTML (szablony Jinja2)static/ — pliki CSS, JS, obrazystatic/css/style.css — arkusz stylów
# app.py
from flask import Flask
app = Flask(__name__)
@app.route('/')
def index():
return 'Witaj w Flasku!'
@app.route('/hello/')
def hello(name):
return f'Cześć, {name}!'
if __name__ == '__main__':
app.run(debug=True)
# Uruchomienie:
# python app.py
# → http://127.0.0.1:5000/
# → http://127.0.0.1:5000/hello/Kasia
Routing to mechanizm łączenia adresów URL z funkcjami Pythona. Dekorator @app.route() rejestruje trasę.
| Zapis | Znaczenie |
|---|---|
@app.route('/') | strona główna |
@app.route('/about') | stały URL |
@app.route('/user/<name>') | parametr jako string |
@app.route('/post/<int:id>') | parametr jako int |
methods=['GET','POST'] | obsługa formularzy |
from flask import Flask, redirect, url_for
app = Flask(__name__)
@app.route('/')
def index():
return 'Strona główna
'
# Parametr tekstowy
@app.route('/user/')
def profil(username):
return f'Profil użytkownika: {username}
'
# Parametr liczbowy
@app.route('/post/')
def wpis(post_id):
return f'Wpis numer: {post_id}
'
# Przekierowanie
@app.route('/stara-strona')
def stara():
return redirect(url_for('index'))
# Kilka URL dla jednej funkcji
@app.route('/info')
@app.route('/o-nas')
def o_nas():
return 'Informacje o nas
'
if __name__ == '__main__':
app.run(debug=True)
Szablony to pliki HTML z wbudowaną logiką Pythona. Przechowujemy je w folderze templates/.
| Składnia Jinja2 | Znaczenie |
|---|---|
{{ zmienna }} | wyświetl wartość zmiennej |
{% if warunek %} | instrukcja warunkowa |
{% for x in lista %} | pętla |
{% extends "base.html" %} | dziedziczenie szablonu |
{% block nazwa %} | blok do nadpisania |
{% include "plik.html" %} | dołączenie pliku |
{{ zmienna|upper }} | filtr — wielkie litery |
return render_template('index.html', dane=dane) — renderuje szablon i przekazuje zmienne. W szablonie dostępne są jako {{ dane }}.
# templates/base.html
"""
<!DOCTYPE html>
<html lang="pl">
<head>
<title>{% block title %}Moja Aplikacja{% endblock %}</title>
</head>
<body>
<nav><a href="/">Strona główna</a></nav>
<main>{% block content %}{% endblock %}</main>
</body>
</html>
"""
# templates/lista.html
"""
{% extends "base.html" %}
{% block title %}Lista uczniów{% endblock %}
{% block content %}
<h1>Uczniowie</h1>
{% if uczniowie %}
<ul>
{% for u in uczniowie %}
<li>{{ u.imie }} — {{ u.wynik }} pkt</li>
{% endfor %}
</ul>
{% else %}
<p>Brak uczniów.</p>
{% endif %}
{% endblock %}
"""
# app.py
from flask import Flask, render_template
app = Flask(__name__)
@app.route('/lista')
def lista():
uczniowie = [
{"imie": "Kasia", "wynik": 95},
{"imie": "Marek", "wynik": 87},
]
return render_template('lista.html',
uczniowie=uczniowie)
Formularze HTML pozwalają użytkownikowi wysyłać dane do serwera. Flask odbiera je przez obiekt request.
from flask import request — obiekt żądania HTTPrequest.method — "GET" lub "POST"request.form['pole'] — dane z formularza POSTrequest.args['param'] — parametry z URL (GET)request.form.get('pole', domyslna) — bezpieczny odczytredirect(url_for(...)). Bez tego odświeżenie strony ponownie wyśle formularz (duplikaty danych).
from flask import Flask, render_template
from flask import request, redirect, url_for
app = Flask(__name__)
# Dane w pamięci (na egzaminie wystarczy lista/słownik)
notatki = []
@app.route('/', methods=['GET', 'POST'])
def index():
if request.method == 'POST':
tresc = request.form.get('tresc', '').strip()
if tresc:
notatki.append(tresc)
return redirect(url_for('index'))
return render_template('index.html',
notatki=notatki)
# templates/index.html
"""
{% extends "base.html" %}
{% block content %}
<h1>Moje notatki</h1>
<form method="POST">
<input type="text" name="tresc"
placeholder="Nowa notatka...">
<button type="submit">Dodaj</button>
</form>
<ul>
{% for notatka in notatki %}
<li>{{ notatka }}</li>
{% endfor %}
</ul>
{% endblock %}
"""
CRUD (Create, Read, Update, Delete) to cztery podstawowe operacje na danych. Na egzaminie INF.04 wystarczy prosta lista lub słownik w pamięci — bez bazy danych.
lista.append()render_template(dane=lista)lista.pop(id)from flask import (Flask, render_template,
request, redirect, url_for)
app = Flask(__name__)
uczniowie = [
{"id": 1, "imie": "Kasia", "wynik": 95},
{"id": 2, "imie": "Marek", "wynik": 87},
]
next_id = 3
@app.route('/')
def index():
return render_template('crud.html',
uczniowie=uczniowie)
@app.route('/dodaj', methods=['POST'])
def dodaj():
global next_id
imie = request.form.get('imie', '').strip()
wynik = int(request.form.get('wynik', 0))
if imie:
uczniowie.append({"id": next_id,
"imie": imie,
"wynik": wynik})
next_id += 1
return redirect(url_for('index'))
@app.route('/usun/')
def usun(uid):
global uczniowie
uczniowie = [u for u in uczniowie
if u['id'] != uid]
return redirect(url_for('index'))
@app.route('/edytuj/',
methods=['GET', 'POST'])
def edytuj(uid):
u = next((x for x in uczniowie
if x['id'] == uid), None)
if not u:
return redirect(url_for('index'))
if request.method == 'POST':
u['imie'] = request.form.get('imie', u['imie'])
u['wynik'] = int(request.form.get('wynik', u['wynik']))
return redirect(url_for('index'))
return render_template('edytuj.html', u=u)
Poniżej kompletne szablony HTML potrzebne do działającego CRUD. Zapisz je w folderze templates/.
base.html — szkielet strony z nawigacjącrud.html — lista + formularz dodawaniaedytuj.html — formularz edycjistatic/css/style.css{{ url_for('nazwa_funkcji') }}. Gdy zmienisz routing — linki w szablonach automatycznie się zaktualizują.
<!-- templates/base.html -->
<!DOCTYPE html>
<html lang="pl">
<head>
<meta charset="UTF-8">
<title>{% block title %}Aplikacja{% endblock %}</title>
<link rel="stylesheet"
href="{{ url_for('static', filename='css/style.css') }}">
</head>
<body>
<header>
<a href="{{ url_for('index') }}">Strona główna</a>
</header>
<main>{% block content %}{% endblock %}</main>
</body>
</html>
<!-- templates/crud.html -->
{% extends "base.html" %}
{% block title %}Lista uczniów{% endblock %}
{% block content %}
<h1>Uczniowie</h1>
<form action="/dodaj" method="POST">
<input type="text" name="imie" placeholder="Imię" required>
<input type="number" name="wynik"
placeholder="Wynik" min="0" max="100" required>
<button type="submit">Dodaj</button>
</form>
<table>
<tr><th>ID</th><th>Imię</th>
<th>Wynik</th><th>Akcje</th></tr>
{% for u in uczniowie %}
<tr>
<td>{{ u.id }}</td>
<td>{{ u.imie }}</td>
<td>{{ u.wynik }}</td>
<td>
<a href="/edytuj/{{ u.id }}">Edytuj</a>
<a href="/usun/{{ u.id }}">Usuń</a>
</td>
</tr>
{% endfor %}
</table>
{% endblock %}
Napisz aplikację Flask z formularzem: dwa pola liczbowe i wybór działania (+, -, *, /). Po wysłaniu formularza wyświetl wynik na tej samej stronie. Obsłuż dzielenie przez zero.
from flask import Flask, render_template, request
app = Flask(__name__)
@app.route('/', methods=['GET', 'POST'])
def kalkulator():
wynik = None
blad = None
if request.method == 'POST':
try:
a = float(request.form['a'])
b = float(request.form['b'])
dzial = request.form['dzialanie']
if dzial == '+': wynik = a + b
elif dzial == '-': wynik = a - b
elif dzial == '*': wynik = a * b
elif dzial == '/':
if b == 0:
blad = "Błąd: dzielenie przez zero!"
else:
wynik = a / b
if wynik is not None:
wynik = round(wynik, 6)
except ValueError:
blad = "Błąd: podaj prawidłowe liczby."
return render_template('kalkulator.html',
wynik=wynik, blad=blad)
if __name__ == '__main__':
app.run(debug=True)
float(request.form['a']) rzuci ValueError jeśli użytkownik wpisze tekst. Łapiemy wyjątek i pokazujemy przyjazny komunikat zamiast strony błędu.
{% if wynik is not none %} i nie wyświetla sekcji wyniku przy pustym formularzu.
Napisz aplikację ToDo: dodawanie zadań przez formularz, oznaczanie jako ukończone (checkbox), usuwanie zadań. Każde zadanie ma tekst i status ukończenia. Wyświetl osobno zadania aktywne i ukończone.
from flask import (Flask, render_template,
request, redirect, url_for)
app = Flask(__name__)
zadania = []
next_id = 1
@app.route('/', methods=['GET', 'POST'])
def index():
if request.method == 'POST':
global next_id
tekst = request.form.get('tekst','').strip()
if tekst:
zadania.append({
'id': next_id,
'tekst': tekst,
'ukonczone': False
})
next_id += 1
return redirect(url_for('index'))
aktywne = [z for z in zadania
if not z['ukonczone']]
ukonczone = [z for z in zadania
if z['ukonczone']]
return render_template('todo.html',
aktywne=aktywne,
ukonczone=ukonczone)
@app.route('/ukoncz/')
def ukoncz(zid):
for z in zadania:
if z['id'] == zid:
z['ukonczone'] = not z['ukonczone']
break
return redirect(url_for('index'))
@app.route('/usun/')
def usun(zid):
global zadania
zadania = [z for z in zadania
if z['id'] != zid]
return redirect(url_for('index'))
if __name__ == '__main__':
app.run(debug=True)
z['ukonczone'] = not z['ukonczone'] — przełącza wartość boolowską. Kliknięcie "ukończ" na ukończonym zadaniu przywraca je do aktywnych.
[z for z in zadania if not z['ukonczone']] — tworzy listę tylko aktywnych zadań. Przekazujemy do szablonu dwie osobne listy.
Zbuduj aplikację webową do zarządzania ocenami uczniów. Strona główna wyświetla listę uczniów ze średnimi. Podstrona ucznia pokazuje jego oceny i umożliwia dodanie nowej. Dane przechowuj w słowniku w pamięci.
from flask import (Flask, render_template,
request, redirect, url_for)
app = Flask(__name__)
dziennik = {
"Kasia": [5, 4, 5, 3, 4],
"Marek": [3, 4, 2, 4, 3],
"Zofia": [5, 5, 4, 5, 5],
}
@app.route('/')
def index():
dane = []
for imie, oceny in dziennik.items():
sr = sum(oceny) / len(oceny) if oceny else 0
dane.append({"imie": imie,
"srednia": round(sr, 2),
"ile": len(oceny)})
dane.sort(key=lambda x: x['srednia'], reverse=True)
return render_template('dziennik.html', uczniowie=dane)
@app.route('/uczen/',
methods=['GET', 'POST'])
def uczen(imie):
if imie not in dziennik:
return redirect(url_for('index'))
if request.method == 'POST':
try:
ocena = int(request.form['ocena'])
if 1 <= ocena <= 6:
dziennik[imie].append(ocena)
except ValueError:
pass
return redirect(url_for('uczen', imie=imie))
oceny = dziennik[imie]
sr = sum(oceny) / len(oceny) if oceny else 0
return render_template('uczen.html',
imie=imie,
oceny=oceny,
srednia=round(sr, 2))
if __name__ == '__main__':
app.run(debug=True)
dziennik = {"Kasia": [5,4,5...]} — klucz to imię, wartość to lista ocen. Prosty i wystarczający model danych dla aplikacji egzaminacyjnej.
index() budujemy listę słowników z przetworzonymi danymi (imię + średnia + ile ocen). Logika obliczania jest w Pythonie, nie w szablonie.
/uczen/Kasia → imie="Kasia" → dziennik["Kasia"]. Sprawdzamy czy uczeń istnieje przed użyciem — inaczej KeyError.
try/except ValueError na konwersji, plus sprawdzenie zakresu 1–6. Zła ocena jest po prostu ignorowana — aplikacja nie crashuje.
Każde zadanie to kompletna mini-aplikacja Flask. Pamiętaj o folderze templates/ i uruchamianiu przez python app.py.
Przelicznik temperatury
Formularz z polem liczbowym i wyborem jednostki (C/F/K). Po wysłaniu wyświetl temperaturę we wszystkich trzech jednostkach.
render_template z wynikamiLicznik odwiedzin
Aplikacja która liczy ile razy odwiedzono stronę główną. Licznik przechowaj w zmiennej globalnej. Dodaj przycisk "Resetuj licznik".
global licznik, osobny route dla resetu z redirectKsiążka adresowa
CRUD dla kontaktów (imię, telefon, email). Lista wszystkich, szczegóły jednego, formularz dodawania, usuwanie. Użyj słownika z ID jako kluczem.
/kontakt/<int:id>, /dodaj, /usun/<int:id>, szablony z Jinja2Quiz z wynikiem
Aplikacja quizowa — wyświetl pytania jedno po drugim (lub wszystkie naraz), zbierz odpowiedzi, oblicz wynik i wyświetl podsumowanie z procentem poprawnych.
request.form dla checkboxów lub radioWyszukiwarka w danych
Masz listę produktów (nazwa, cena, kategoria). Formularz GET z polem wyszukiwania i filtrem kategorii. Wyświetl pasujące wyniki — szukaj w nazwie bez rozróżniania wielkości liter.
request.args.get('q',''), filtrowanie przez list comprehension, method="GET" w formularzuAplikacja z plikiem CSV
Aplikacja wczytuje dane uczniów z pliku CSV przy starcie. Pozwala przeglądać, dodawać i usuwać rekordy. Po każdej zmianie zapisuje zaktualizowany plik CSV.
app.py poza routami, po modyfikacji zapisz przez csv.writer