Python in Flask¶
Zakaj Flask¶
Flask je majhen in pregleden spletni okvir, zato je zelo primeren za poučevanje osnov spletnih aplikacij. Pri njem dijak zelo hitro vidi:
- kje nastane aplikacija,
- kako določimo poti,
- kako vrnemo odgovor,
- kako prikažemo HTML predlogo,
- kako preberemo obrazec,
- kako dostopamo do baze.
Ta preglednost je v učnem okolju velika prednost.
Kaj mora dijak razumeti¶
Pri Flasku ni cilj, da osvoji celoten ekosistem. Cilj je, da razume temeljni tok:
To je osnovni hrbet vsake male spletne aplikacije.
Najmanjša možna aplikacija¶
from flask import Flask
app = Flask(__name__)
@app.route("/")
def index():
return "Pozdrav iz Flask aplikacije!"
if __name__ == "__main__":
app.run(debug=True)
Ta primer pokaže tri ključne ideje:
- aplikacijo ustvarimo z
Flask(__name__), @app.route("/")pove, katera funkcija odgovori na pot,- funkcija vrne vsebino odgovora.
Kaj je route¶
Route je povezava med URL potjo in Python funkcijo.
@app.route("/")
def index():
return "Domov"
@app.route("/o-projektu")
def o_projektu():
return "Opis projekta"
To je didaktično odlično, ker je razmerje med potjo in obnašanjem neposredno.
Vračanje HTML predlog¶
Resna aplikacija običajno ne vrača samo nizov, ampak HTML predloge. Flask za to uporablja Jinja.
Struktura projekta:
projekt/
├─ app.py
├─ templates/
│ ├─ base.html
│ ├─ index.html
│ └─ dodaj.html
└─ static/
└─ style.css
app.py:
from flask import Flask, render_template
app = Flask(__name__)
@app.route("/")
def index():
knjige = [
{"naslov": "Alamut", "avtor": "Vladimir Bartol"},
{"naslov": "1984", "avtor": "George Orwell"},
]
return render_template("index.html", knjige=knjige)
templates/index.html:
<!doctype html>
<html lang="sl">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Knjižnica</title>
</head>
<body>
<h1>Seznam knjig</h1>
<ul>
{% for knjiga in knjige %}
<li>{{ knjiga.naslov }} – {{ knjiga.avtor }}</li>
{% endfor %}
</ul>
</body>
</html>
Zakaj so predloge pomembne¶
Predloge pomagajo ločiti:
- logiko in podatke v Pythonu,
- prikaz v HTML-ju.
To je ena prvih res zdravih arhitekturnih navad.
Dedovanje predlog¶
Ko projekt raste, ni dobro kopirati celotne glave in noge v vsako datoteko. Bolje je uporabiti osnovno predlogo.
templates/base.html:
<!doctype html>
<html lang="sl">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{% block title %}Knjižnica{% endblock %}</title>
<link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}">
</head>
<body>
<header>
<h1>Moja knjižnica</h1>
<nav>
<a href="{{ url_for('index') }}">Domov</a>
<a href="{{ url_for('dodaj') }}">Dodaj knjigo</a>
</nav>
</header>
<main>
{% block content %}{% endblock %}
</main>
</body>
</html>
templates/index.html:
{% extends "base.html" %}
{% block title %}Seznam knjig{% endblock %}
{% block content %}
<h2>Seznam knjig</h2>
<ul>
{% for knjiga in knjige %}
<li>{{ knjiga["naslov"] }} – {{ knjiga["avtor"] }}</li>
{% endfor %}
</ul>
{% endblock %}
S tem dijak hitro vidi, kako se zmanjša podvajanje.
Branje podatkov iz obrazca¶
from flask import Flask, render_template, request
app = Flask(__name__)
@app.route("/dodaj", methods=["GET", "POST"])
def dodaj():
if request.method == "POST":
naslov = request.form.get("naslov", "").strip()
avtor = request.form.get("avtor", "").strip()
return f"Prejeli smo: {naslov} – {avtor}"
return render_template("dodaj.html")
S tem primerom lahko razložiš:
- ista route lahko obravnava prikaz in oddajo obrazca,
request.methodpove, kaj se dogaja,request.formvsebuje podatke iz obrazca,- podatke je treba očistiti in preveriti.
Preusmeritev po uspešni oddaji¶
Po uspešni oddaji je pogosto bolje uporabnika preusmeriti na seznam ali drugo stran.
from flask import redirect, url_for
@app.route("/shrani", methods=["POST"])
def shrani():
# shrani podatke
return redirect(url_for("index"))
To je dober trenutek, da razložiš razliko med:
- vračanjem strani neposredno,
- preusmeritvijo na drugo pot.
url_for¶
url_for je zelo koristna funkcija, ker ne lepimo poti ročno po projektu.
Namesto:
lahko uporabimo:
Prednost je v tem, da je povezava vezana na ime route, ne na ročno napisan niz.
Delo s statičnimi datotekami¶
Za CSS in slike Flask uporablja mapo static.
To je ena tistih stvari, ki jih je bolje naučiti pravilno že prvič, ker sicer dijaki hitro končajo v ugibanju poti.
Povezava s SQLite¶
Flask in SQLite se za učni projekt zelo lepo ujemata.
Primer povezave:
import sqlite3
from flask import Flask, g
app = Flask(__name__)
DATABASE = "knjiznica.db"
def get_db():
if "db" not in g:
g.db = sqlite3.connect(DATABASE)
g.db.row_factory = sqlite3.Row
return g.db
@app.teardown_appcontext
def close_db(exception):
db = g.pop("db", None)
if db is not None:
db.close()
Ta vzorec je zelo uporaben, ker:
- povezavo odpre po potrebi,
- jo zapre ob koncu zahtevka,
- omogoči bolj urejeno delo z bazo.
Branje podatkov iz baze¶
@app.route("/")
def index():
db = get_db()
knjige = db.execute(
"SELECT id, naslov, avtor, leto FROM knjige ORDER BY naslov"
).fetchall()
return render_template("index.html", knjige=knjige)
Shranjevanje podatkov v bazo¶
@app.route("/dodaj", methods=["GET", "POST"])
def dodaj():
if request.method == "POST":
naslov = request.form.get("naslov", "").strip()
avtor = request.form.get("avtor", "").strip()
leto = request.form.get("leto", "").strip()
if not naslov or not avtor:
return render_template(
"dodaj.html",
napaka="Naslov in avtor sta obvezna."
)
db = get_db()
db.execute(
"INSERT INTO knjige (naslov, avtor, leto) VALUES (?, ?, ?)",
(naslov, avtor, leto or None),
)
db.commit()
return redirect(url_for("index"))
return render_template("dodaj.html")
To je odličen “aha” trenutek predmeta, ker dijak vidi celoten tok od obrazca do baze.
Organizacija projekta¶
Za majhen projekt zadošča čista osnovna struktura:
projekt/
├─ app.py
├─ schema.sql
├─ knjiznica.db
├─ templates/
│ ├─ base.html
│ ├─ index.html
│ └─ dodaj.html
└─ static/
└─ style.css
Ko je struktura jasna, je precej manj kaosa in precej manj “kam pa sem to sploh shranil”.
Debug način¶
Pri razvoju pogosto uporabljaš:
To pomaga pri razvoju, ker:
- hitreje vidiš napake,
- se aplikacija samodejno ponovno naloži,
- lažje slediš izpisu.
Pomembno pa je, da dijaki razumejo: to je razvojni način, ne produkcijska rešitev.
Pogoste napake¶
Napačna metoda v route¶
Obrazec pošilja POST, route pa ne podpira POST.
Napačno ime polja¶
request.form.get("naslov") ne bo našel vrednosti, če je name v HTML-ju drugačen.
Predloga ni na pravem mestu¶
Flask pričakuje mapo templates.
CSS se ne naloži¶
Statične datoteke morajo biti v static.
Manjka commit()¶
Podatki se ne shranijo trajno.
Kontrolni seznam¶
1. Ali route obstaja?
2. Ali route podpira pravo metodo?
3. Ali HTML polja uporabljajo prava name imena?
4. Ali je predloga v mapi templates?
5. Ali so statične datoteke v static?
6. Ali pri INSERT uporabljam placeholderje?
7. Ali po spremembi baze izvedem commit?
Kaj naj dijak zna po tem poglavju¶
- ustvariti osnovno Flask aplikacijo,
- definirati route,
- vrniti HTML predlogo,
- prebrati podatke iz obrazca,
- preusmeriti uporabnika po oddaji,
- povezati aplikacijo z SQLite,
- prikazati podatke iz baze na strani.
Najmočnejši del poglavja
Ko dijak vidi, da obrazec pošlje podatke, Flask jih prebere, SQLite jih shrani in se nato prikaže posodobljen seznam, se večina prej ločenih tem končno združi v eno razumljivo sliko.