První webová stránka ve Flasku

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.

Štítky
Úroveň znalostí
Začátečník