Flask je webový mikroframework. Na rozdíl od druhého populárního frameworku, kterým je Django, v sobě nemá knihovny pro ORM nebo zpracování formulářů. Místo hromady dalších knihoven a tříd, například pro připojení k databázi, si zakládá na tom být malinký, jednoduchý a rozšířitelný. S Flaskem dostanete k ruce základní nástroje a utility pro zpracování requestů, cookies, url nebo šablon, nic víc. Je pak pouze na vás, jaké další knihovny pro zpracování formulářů, připojení k databázi nebo ORM použijete. 🙂
Ahoj světe! ve Flasku
Začneme standardně "Hello world" programem. Ze všeho nejdřív si budeme muset Flask nainstalovat. Pro instalaci využijeme (momentálně) nejoblíbenější balíčkovací systém pip
a nezapomeneme vytvořit virtual env, abychom si odizolovali nainstalované knihovny v lokálním adresáři a ne v globálním systémovém.
virtualenv -p python3 venv
source ./venv/bin/activate
pip install flask
Náš první program může vypadat takto:
from flask import Flask
app = Flask(__name__)
app.config["DEBUG"] = True
@app.route("/")
def index():
return "Ahoj světe!"
if __name__ == "__main__":
app.run()
Vyrobíme novou instanci třídy Flask
a připravíme handler index()
. Díky dekorátoru @app.route
jsme schopni Flasku snadno říct, že tento handler bude obsluhovat url "/"
, tj. vstupní stránku. Skript si pojmenujeme jako manage.py
a můžeme jej zkusit pomocí python manage.py
spustit. Po spuštění se nám objeví v terminálu něco takového:
* Serving Flask app 'manage' (lazy loading)
* Environment: production
WARNING: This is a development server. Do not use it in a production deployment.
Use a production WSGI server instead.
* Debug mode: off
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
Všimněte si především varování ohledně vývojového serveru. Kdybychom aplikaci takovýmto způsobem chtěli spustit na produkčním serveru, pravděpodobně by nám nevydržela moc dlouho běžet, byla by pomalá a nezvládla by větší nápor. Tímto způsobem jsme totiž spustili interní vývojový server, který není vhodný pro produkční použití. Pro spuštění aplikace v produkčním prostředí je nutné se porozhlídnout pro aplikačních serverech jako uwsgi nebo gunicorn.
Když si teď ve webovém prohlížeči otevřeme url http://127.0.0.1:5000
, měla by se nám otevřít jednoduchá webová stránka s textem "Ahoj světe!".
Vykreslujeme šablonu
Flask se nainstaluje rovnou s šablonovacím systémem Jinja2. Připravíme si jednoduchou šablonu a uložíme ji do adresáře templates
jako index.html
:
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Ahoj světe!</title>
</head>
<body>
<div class="content">
<h1>Ahoj světe!</h1>
<p>Vidím, že chceš načíst článek {{article_slug}}.</p>
</div>
<div class="menu">
<ul>
{% for item in items %}
<li>{{item}}</li>
{% endfor %}
</ul>
</div>
</body>
</html>
Upravíme náš python skript tak, aby dokázal načíst šablonu a vyrenderovat proměnné {{article_slug}}
a {{items}}
s daty, které do ní pošleme. Kromě toho upravíme i dekorátor app.route
, kterému můžeme říct, jak naše url bude vypadat a ten podle potřeby z jednotlivých částí url udělá proměnné (article_slug
), které pod stejným názvem pošle do našeho handleru. V něm si tuto hodnotu dokážeme vyčíst a například na základě strojového názvu článku (slugu) nebo jeho id, pokud by bylo v url, z databáze načíst příslušný článek a poslat šabloně k vyrenderování.
from flask import Flask
from flask import render_template
app = Flask(__name__)
app.config["DEBUG"] = True
@app.route("/<string:article_slug>")
def index(article_slug):
return render_template("index.html", **{
"article_slug": article_slug,
"items": ["První", "Druhá", "Třetí", "Čtvrtá"]
})
if __name__ == "__main__":
app.run()
Po spuštění aplikace přes python manage.py
se nám opět nastartuje vývojový server a po zadání url http://127.0.0.1:5000/ahoj
do okna prohlížeče se zobrazí webová stránka, která vychází z naší šablony a místo nahrazovacích řetězců/proměnných vykreslí data, která jsme jí poslali.
A teď pěkněji
Celá komplexní webová stránka se nedá napsat v jednom manage.py
souboru... teda, samozřejmě dá, ale čitelnost, udržitelnost a praktičnost bude mizivá 🙂. Vždy je dobré myslet stylem "rozděl a panuj". Co se dá, tak dělit na logické části a vrstvy. Ať už je to backend vs. frontend, MVC architektura nebo dílčí rozdělení velkého skriptu na menší části. V následujícím příkladu nebudeme v rámci zachování jednoduchosti řešit konkrétní datovou vrstvu nebo připojení k databázi, spíš si zkusíme naznačit, jak by to mohlo vypadat.
Budeme chtít oddělit konfiguraci aplikace, views (v našem případě handlery obsluhující nějakou url) a připravit middleware pro připojení k databázi.
projekt/
├── requirements.txt
├── manage.py
├── projekt
│ ├── __init__.py
│ ├── config.py
│ ├── middlewares
│ │ ├── __init__.py
│ │ └── db.py
│ └── views
│ ├── __init__.py
│ ├── base.py
│ └── index.py
└── templates
└── index.html
💾 Projekt ke stažení: hello-world-project-flask.zip.
Kořenový adresář projektu obsahuje soubor requirements.txt
, ve kterém jsou vypsány všechny závislosti projektu. V našem případě je to pouze flask
, který se pomocí pip install -r requirements.txt
nainstalujete. Pomocí souboru manage.py
tentokrát pouze spouštíme vývojový server a nastavujeme cestu k šablonám, nic víc. Ve chvíli, kdy bychom náš projekt chtěli spustit v produkci, nejspíš by nám vedle manage.py
přistál například ještě uwsgi.py
jakožto entrypoint pro uwsgi aplikační server. Všimněte si, že adresář templates
není zanořen v adresáři projekt/projekt
. Je to z toho důvodu, že šablony, i když nepochybně patří do našeho projektu, nejsou kód a neměly by být součástí naší hlavní knihovny.
V hlavní knihovně projekt/projekt
máme vytvořeny adresáře pro views (v našem případě handlery jednotlivých url), middlewares (kód, který chceme spustit při každé obsluze requestu) a config.py
, což je soubor s konfigurací.
Aplikaci chceme spouštět v různých prostředích - vývojovém, předprodukčním, produkčním - a pro každé prostředí můžeme mít konfiguraci mírně odlišnou. Typickým příkladem je databáze a připojení k ní. Ve vývojové databázi mohou být nesmysly a testovací data, které rozhodně nechceme vydávat ven uživatelům. A naopak, je přísně zakázáno se z lokálního stroje dostat do produkční databáze a nešťastnou náhodou tam vyrobit nějakou neplechu. Konfigurační soubor proto obsahuje několik tříd, které ze sebe dědí a jejich atributy tak můžeme podle potřeby přepisovat. Ve funkci configure_app
zkoumáme, zda existuje env proměnná ENVIRONMENT
a podle její hodnoty zvolíme správnou konfigurační třídu, pomocí které nastavíme aplikaci. Tento způsob konfigurace aplikace bude dobře fungovat i pro aplikaci, kterou spustíte v Docker kontejneru.
Za zmínku stojí ještě soubor middlewares/db.py
. V něm máme připravenou funkci wrap
, která přijímá flask aplikaci a díky dekorátoru @app.before_request
pak během zpracování každého requestu přidá do globálního flask kontextu (from flask import g
) konektor k databázi získaný z funkce get_db
. Dalším příkladem middlewaru může být nastavení logování nebo přihlášení uživatele, kterého si uložíme do "g
" a kdekoliv v kódu si ho pak podle potřeby načteme.