Webová aplikace v Pythonu s Flask frameworkem


Notice: Trying to access array offset on value of type bool in /var/www/clients/client14/web43/web/wp-content/plugins/wp-word-count/public/class-wpwc-public.php on line 123

Slovo úvodem

Pokud jste se rozhodli použít jazyk Python k programování webových aplikací, nejspíše se právě porozhlížíte po nějakém pěkném frameworku, který vám může usnadnit práci. Ve světě Pythonu možná uslyšíte ze všech stran o Django frameworku, který je svým významem přirovnáván k Rails s jazykem Ruby.

Ne na všechny úlohy se ale Django hodí a ne všem zcela vyhovuje. Osobně například preferuji sofistikovanou ORM knihovnu SQLAlchemy namísto řešení, které je používáne v Djangu. Stejně tak raději používám šablonovací systém Jinja2, než obdobný systém v Django, který je dle mého názoru méně přehledný (např. funkce lze volat bez kulatých závorek) a dle testů i 10-20x pomalejší. Jako vhodná alternativa se může jevit právě Flask mikroframework. Elegantní, odlehčený a flexibilní framework, který dělá jen to, co má a zbytek záleží jen a jen na vás. Samotný Flask pochází z dílny výborného programátora Armina Ronachera, který je rovněž auterem již zmiňovaného Jinja2 a také knihovny Werkzeug sloužící jako nadstavba WSGI. Flask vlastně není nic jiného, než „lepidlo“ pro Werkzeug a Jinja2, ke kterým přidává API a nové funkce pro snadnější tvorbu webových aplikací. Tento seriál se vám pokusí představit jeho možnosti…

První příklad

Nejprve si nainstalujte knihovnu Flask (pokud jste tak již neučinili). To můžete například udělat následujícím příkazem

pip install flask

pod právy roota, nebo postupujte podle tohoto návodu pro instalaci.

Pojďme rovnou na naši první aplikaci. „Ahoj světe“ je ohrané, tady máte něco hezčího (doplňte si dle libosti, ale Opava je stejně nejhezčí :P)…

from flask import Flask
 
app = Flask(__name__)
 
@app.route('/')
def index():
    return 'Ahoj Opavo!'
 
if __name__ == '__main__':
    app.run()

Pokud tento kód spustíte (používejte verzi Pythonu 2.x) jako hlavní modul, např. „python ahoj.py“ , uvidíte pravděpodobně tuto hlášku:

* Running on http://127.0.0.1:5000/

Pokud tuto hlášku uvidíte, můžete otevřít svůj webový prohlížeč a zadat příslušnou adresu. Flask za vás vytvořil testovací webový server.

Z výše uvedeného zdrojového kódu můžete postřehnout pár věcí:

Pro vaši aplikaci vytváříte instanci třídy Flask, která je rovněž funktorem a tak ji můžete použít rovněž jako WSGI handler. Obsluhy URL adres můžete definovat pomocí dekorátorů funkcí. Funkce index() se zavolá jako obsluha pro úvodní stránku (GET /). Vypíše se navrácený řetězec, přičemž Flask řeší HTTP hlavičky za vás. Pro sofistikovanější odpovědi můžete použít komplexnější objekt Response (někdy příště si o něm řekneme) nebo render_template(), viz níže. Adresář kontaktů

Pustíme se rovnou do další aplikace, na které si předvedeme, jak můžeme ve Flasku pracovat se šablonami, statickými soubory, URL adresami a zpracovávat obdržená data z formulářů. Jedná se o aplikaci pro jednoduchou správu kontaktních informací.

Budeme mít následující soubory:

  • contacts.py – logika aplikace
  • templates/layout.html – rovržení stránky
  • templates/list.html – seznam kontaktů (úvodní stránka)
  • templates/form.html – formulář pro úpravu/vytvoření kontaktu
  • static/style.css – základní definice stylů Celou aplikaci si můžete stáhnout na konci tohoto článku v příloze. V následující části se podíváme na to, co jednotlivé řádky kódu znamenají.

contacts.py

# -*- coding: utf-8 -*-
 
from flask import Flask, url_for, render_template, redirect, \
    request, flash
 
app = Flask(__name__)

Znakovou sadu souboru nastavujeme na UTF-8 proto, že budeme používat hlášení v českém jazyce. Tento řádek nicméně doporučuji vkládat automaticky do všech vašich .py souborů, předejdete tak možným problémům v budoucnosti. V kódu dále naimportujeme potřebné funkce a vytvoříme obejekt naší aplikace – app.

app.debug = True

Nastavením atributu app.debug na True docílíme dvou věcí:

  1. V případě výskytu neobsloužené výjimky, bude zobrazena interaktivní ladící konzole přímo v prohlížeči, ve které budeme moci zjistit, co výjimku vyvolalo.
  2. Druhou funcí ladícího režimu je znovu-načtení aplikace v případě, že došlo ke změně kódu v některém z použitých skriptů. Hlídají se tedy i skripty, které hlavní modul importuje.
app.static_path = 'static'
app.template_folder = 'templates'

Nyní jsme Flasku sdělili, ve kterých adresářích se nacházejí naše statické soubory (css, javascripty, obrázky) a šablony.

app.secret_key = 'sfj3defk'

Nastavili jsme privátní klíč, který Flask používá pro šifrovací účely (např. pro generování bezpečnostních CSRF tokenů pro formuláře).

contacts = []

Náš seznam kontaktů budeme ukládat do globální proměnné typu seznam. V našem ukázkovém případě to nevadí, neboť naše aplikace je tvořena pouze jedním procesem (máme tak jistotu, že budeme vždy pracovat s téže proměnnou). Samozřejmě dojde ke ztrátě dat v případě, že naši aplikaci ukončíme. Můžete však přepsat kód tak, aby využíval stálé úložiště 🙂

@app.route('/', endpoint='index')
def index():
    return render_template('list.html', 
            contacts=list(enumerate(contacts)))

První stránka webu, seznam kontaktů, je opět uvozena definicí URL cesty s koncovým bodem „index“. Koncový bod je jednoduše řečeno nějaký alias pro určitou funkcionalitu webu, na který se můžete odvolávat. Funkce url_for nám vrátí URL k zadanámu koncovému bodu. Samozřejmě není vyžadováno aby „endpointy“ byly vždy zadávány, ale je silně doporučováno je využívat. V pozdějších fázích vývoje vám mohou zpřehlednit práci.

@app.route('/delete/<int:index>', endpoint='delete')
def delete_contact(index):
    try:
        r = contacts.pop(index)
        flash(u'Záznam %s byl smazán.' % r['name'])
    except IndexError:
        flash(u'Tento záznam neexistuje!')
    return redirect(url_for('index'))

Tato funkce má na starost vymázání záznamu ze seznamu. Novinkou je definice URL, konkrétně pak část <int:index>. Jak správně tušíte, argumenty, které chceme předávat funkci z URL adresy označujeme <konvertor:nazev>. Pokud konvertor (de facto kontrola datového typu) nezadáte, použije se jako výchozí konvertor řetězce (všechny znaky s výjimkou /). Mimo to můžete použít ještě float pro desetinné číslo a path pro cestu k souboru (všechny znaky vč. /). Můžete si dokonce nadefinovat vlastní konvertor, ale o tom někdy jindy.

Na řádku 23. a 25. je pak použita funkce flash, která slouží pro předávání informačních zpráv (hlášek) pro uživatele. Tyto zprávy můžete následně získat metodou get_flashed_messages (viz šablona templates/layout.html, řádek 15). Pokud potřebujete rozlišit typ zprávy, můžete využít klíčovaný argument category.

@app.route('/new', methods=('get','post'), endpoint='new')
def new_contact():
    if request.method == 'POST':
        if 'name' not in request.form or \
            not len(request.form['name']):
            flash(u'Zadání jména je povinné', 'error')
            return render_template('form.html')
        contact = {'name': request.form['name'], 
                   'email': request.form['email'],
                   'phone': request.form['phone']}
        contacts.append(contact)
        flash(u'Kontakt %s byl úspěšně přidán.' %
              request.form['name'])
        return redirect(url_for('index'))
    return render_template('form.html')

Konečně poslední funkcí je funkce pro zadání nového kontaktu do adresáře. Všimněte si opět definice URL adresy a parametru methods. Ta Flasku říká, které metody daná URL adresa podporuje. Kdybychom nepřidali metodu POST, funkce by byla po odeslání formuláře nedostupná.

Byl-li formulář odeslán, funkce vybere přijatá formulářová data (slovník request.form), provede základní validaci a vytvoří nový záznam v adresáři kontaktů. O úspěchu nebo chybě pěkně vyrozumíme uživatele. V dalších článcích si pak ukážeme efektivnější způsoby zpracovávání formulářů, například pomocí rozšíření Flask-WTF.

if __name__ == '__main__':
    app.run()

Spustíme aplikaci.

Šablony HTML

V naší aplikací jsme využili zajímavé funkce, která umožňuje dědění šablon. Pro ty co neví o co se jedná. Představme si základní šablonu určující základní layout stránky:

<title>{%block title %}{% endblock %}</title>
{% block content %}{% endblock %}

Pak můžeme vytvářet odvozené šablony vycházející z předchozí:

{% extends 'layout.html' %}
{% block title %}Toto je titulek stránky {% endblock %}
{% block content %} 
<h1>Nadpis</h1>
<p>Toto je obsah stránky.</p>
{% endblock %}

Bližší podrobnosti o možnostech šablonovacího systému Jinja2 najdete v dokumentaci. Vřele doporučuji si ji alespoň projít, neboť možnosti jsou opravdu obrovské. V tomto článku se šablonami nebudu zabývat, protože by toto téma vystačilo na samostatný článek a už tak je tento dlouhý až až 🙂

Závěr

Gratuluji, dočetli jste článek až do konce 🙂 Nebývá mi než doufat, že jsem vás víc motivoval, než odradil. Flask je opravdu velice dobrý kus softwaru s velice kvalitní koncepcí (v jednoduchosti je síla). Pokud tedy potřebujete psát webové aplikace v Pythonu s co možná největší volností, ale zároveň chcete mít kvalitní minimalistický framework, je Flask přesně to, co hledáte. Navíc jeho obliba v poslední době prudce roste a možná se dočkáme chvíle, kdy bude stát bok po boku takovým velikánům jako je Django…

Sdílet na sociálních sítích