Flask
- Hello World
- Flask omalle koneelle
- Reititys ja osoitteet
- Staattiset tiedostot
- request
- Edelleenohjaus
- response
- Flask ja Jinja-templatet
- session
- Lisätietoa
Flask on mikrosovelluskehys jolla voidaan tehdä web-sovelluksia.
Flask perustuu WerkZeug WSGI-kirjastoon ja Jinja-kirjastoon
WSGI (Web Server Gateway Interface) on Pythonin oma rajapinta web-sovellusten toteuttamiseen. Vrt. CGI
Flaskin toiminta on helppo oppia Quickstart-tutoriaalista. Tässä materiaalissa käydään pääpiirteissään lapi Quickstart-tutoriaalin sisältö.
Hello World
Flaskin asennus- ja käyttötapa riippuu ympäristöstä. Esim. Jyväskylän yliopiston users.jyu.fi-palvelimella Flaskia on käytettävä CGI-sovelluksena. Tämä ei ole kovin tehokas tapa mutta riittää hyvin Flaskilla harjoitteluun. Flaskin käyttöönottaminen users.jyu.fi-palvelimella ja pythonanywhere-palvelussa käydään läpi ohjaus 1-tehtävissä. Flaskissa on mukana oma pieni www-palvelin, jota voi käyttää perusasioiden testailuun.
Minimaalinen Flask-sovellus
# importoidaan Flask-luokka ja luodaan siitä samantien esiintymä
from flask import Flask
app = Flask(__name__)
# Kerrotaan Flaskille mitä osoitetta seuraava funktio vastaa
@app.route('/')
def hello_world():
return 'Hello World!'
Flask omalle koneelle
- Asenna Python 3.10
- Katso tarkemmat ohjeet ohjaustehtävästä
Esimerkki
Videolla näytetty koodiesimerkki.
from flask import Flask, request, Response
app = Flask(__name__)
@app.route('/testi') #tämä rivi kertoo osoitteen, josta tämä sovellus löytyy
def hello_world():
return "<h2>foobar</h2>"
@app.route('/env')
def env():
txt = ""
for key in request.environ.keys():
txt += '%s\t:\t%s\n' % (key, request.environ[key])
return Response(txt, mimetype="text/plain;charset=UTF-8")
@app.route('/args')
def args():
txt = ""
for key in request.args.keys():
txt += '%s\t=\t%s\n' % (key, request.args[key])
return Response("<h1>args</h1>" + txt, mimetype="text/plain;charset=UTF-8")
Reititys ja osoitteet
Flask käyttää Pythonin decorator-funktiota määrittelemään mikä osoite viittaa mihinkäkin funktioon.
# vastaa osoitteeseen /hello
@app.route('/hello')
def hello():
return 'Hello World'
Osoitteissa voi olla myös muuttujia. Osoitteessa oleva muuttuja annetaan funktiolle parametrina.
@app.route('/user/<username>')
def show_user_profile(username):
# show the user profile for that user
return 'User %s' % username
Muuttujille on olemassa myös pari convertteria. Nämä eivät kelpuuta vääränlaisia arvoja
| string | merkkijonot ilman /-merkkiä (oletus) |
|---|---|
| int | Kokonaisluvut |
| float | Liukuluvut |
| path | Polku, joka kelpuuttaa myös /-merkit |
@app.route('/post/<int:post_id>')
def show_post(post_id):
# show the post with the given id, the id is an integer
return 'Post %d' % post_id
route-dekoraattorilla kerrotaan myös mitä HTTP-metodeja kyseinen funktio ymmärtää:
# kelpuutetaan sekä GET että POST -metodit. GET on aina oletuksena kelvollinen
@app.route('/login', methods=['GET', 'POST'])
def login():
if request.method == 'POST':
do_the_login()
else:
show_the_login_form()
Samaan funktioon voi osoittaa useita erilaisia reittejä:
# vastaa osoitteeseen /hello
@app.route('/hello/<name>')
@app.route('/hello')
def hello(name=None):
if name:
return 'Hello ' + name
return 'Hello World'
Osoitteet
Flaskin käyttämissä osoitteissa on merkitystä käyttääkö osoitteen lopussa /-merkkiä vai ei:
# tätä voi kutsua osoitteilla /projects ja /projects/. Ensimmäinen ohjaantuu jälkimmäiseen
@app.route('/projects/')
def projects():
return 'The project page'
# Tätä voi käyttää vain muodossa /about
@app.route('/about')
def about():
return 'The about page'
Osoitteita voi muodostaa url_for-funktiolla:
# seuraava rivi käskee Flaskin suorittaa komennot kuin www-selainpyynnön tapauksessa vaikka
# sovellusta oikeasti testataan komentoriviltä
with app.test_request_context():
print url_for('projects') # tulostaa /projects/
print url_for('about') # tulostaa /about
Kannattaa käyttää url_for-funktiota ennemmin kuin hardkoodata osoitteet kaikkialle.
Staattiset tiedostot
Flask-sovelluksessa voi staattiset tiedostot (css, javascript, kuvat jne.) sijoittaa statickansioon
request
request-objekti sisältää www-selaimelta lähetetyt tiedot ja muut http-pyyntöön liittyvät tiedot. Request-objekti tulee Werkzeug-kirjaston kautta.
from flask import request
Request-objektin tärkeimmät ominaisuudet ovat:
- request.args GET-metodilla eli URLissa tulleet parametrit
- request.base_url Sivun polku ilman querystring-osaa eli ?-merkin jälkeistä osuutta
- request.charset Pyynnössä käytetty merkistö
- request.cookies Mahdolliset evästeet dict-muodossa
- request.files Mahdolliset tiedostot
- request.form POST-metodilla tulleet parametrit
- request.full_path Koko sivun polku mukaanlukien querystring
- request.method Käytetty metodi (GET, POST jne.)
- request.path Polku
- request.query_string Pyynnön querystring-osa
- request.remote_addr Asiakkaan IP-osoite
- request.remote_user Asiakkaan mahdollinen käyttäjätunnus
- request.values request.args ja request.form sisältö yhdistettynä
Lomakeparametreja käsiteltäessä on otettava kiinni KeyError, jos pyydettyä parametriä ei löydykkään:
@app.route('/kirjaudu', methods=['POST', 'GET'])
def kirjaudu():
if request.method == 'POST':
try:
if tarkista(request.form['username'],
request.form['password']):
return kirjaudu_sisaan(request.form['username'])
else:
error = 'Virheellinen tunnus tai salasana'
except KeyError:
error = 'Virheellinen lomake'
#tämä suoritetaan, jos kirjautuminen ei onnistu
return render_template('kirjaudu.html', error=error)
Voidaan käyttää myös samoja metodeja kuin cgi.fieldstoragen yhteydessä:
# toisena parametrina voidaan antaa oletusarvo jota käytetään, jos usernamea ei ole annettu
username = request.form.get("username", "")
# kolmantena parametrina voi olla kentän tyyppi
# jos arvo ei kelpaa tyypiksi, palautetaan oletusarvo
lkm = request.form.get("lkm", 1, type=int)
# listat toimivat kuten cgi.fieldstoragen kanssa
lista = request.form.getlist("arvo") # palauttaa aina listan. Jos arvoja ei ole, niin lista on tyhjä
Edelleenohjaus
Edelleenohjaus hoituu redirect-funktiolla:
from flask import abort, redirect, url_for
@app.route('/')
def index():
return redirect(url_for('login'))
response
Flask tekee automaattisesti response-tyyppisen objektin näkymän palauttamasta merkkijonosta. Response-objekti sisältää kaiken tarpeellisen jotta voidaan muodostaa sopiva vastaus selaimelle. Jos tarvii muokata vastausta, niin muodostetaan response-objekti itse, muokataan sitä ja vasta sitten palautetaan response:
from flask import request, response, make_response
@app.route('/')
def index():
# 200 tarkoittaa http-protokollan vastausta jossa kaikki OK. Tarvittaessa
# voidaan tehdä esim. oma virhesivu (404) etc.
resp = make_response(render_template('index.html'), 200)
# keksitään oma http-otsake
resp.headers['X-Foobar'] = 'Testi'
# Määritetään merkistö
resp.charset = "UTF-8"
# Määritetään mimetyyppi
resp.mimetype = "text/html"
return resp
@app.route('/text')
def text():
resp = make_response("foobar. palautetaan pelkkää tekstiä", 200)
# Määritetään merkistö
resp.charset = "UTF-8"
# Määritetään mimetyyppi
resp.mimetype = "text/plain"
return resp
Kts. Werkzeug Response
Flask ja Jinja-templatet
Yhtenäisen sivuston eri sivuilla on usein samankaltaisia osia, jotka joudutaan jokaisella sivulla liittämään includella tai copy&pastella jokaiselle sivulle. Näitä osia ovat esimerkiksi navigoinnit ja autentikoinnista huolehtiminen. Entäpä, jos joltain sivulta tärkeitä kuten käyttäjätarkistus unohtuukin?
Templateilla (eli sivupohjilla) saavutetaan seuraavia etuja:
- Yhteiset osat kootaan samaan paikkaan
- Yhteiset osat liitetään automaattisesti sivulle
- Sivun tekijän tarvitsee keskittyä ainoastaan sisältöosaan
- Voidaan myös pyrkiä erottamaan ohjelmalogiikka esitystavasta.
- Erotetaan SQL:t ja ohjelmakoodit muualle
- Sivun sisällön, ulkoasun ja bisneslogiikan tuottajat voivat tehdä työtä erikseen
- Ei kannata kuitenkaan pyörää keksiä uudelleen - näiden välillä on aina jonkinlaisia kytköksiä
Sivun sisältämää html-koodia ei pidä kirjoittaa suoraan python-ohjelman sisään, koska silloin tuloksena on ylläpitokelvoton koodin ja html:n sekasotku. Pyritään MVC-malliin, jossa ohjelman toimintalogiikka (controller) pidetään erossa näkymästä (view), eli www-sivun sisällöstä. Tämä onnistuu helposti käyttämällä templateja. Yksi käytetyimpiä on Jinja.
Pyri tekemään python-koodissa kaikki mahdollisimman valmiiksi eli pidä jinja-koodi mahdollisimman yksinkertaisena
Flask käyttää Jinja-templateja.
from flask import render_template
@app.route('/hello/')
def hello(name=None):
return render_template('hello.html', name=name)
Templateja etsitään "templates"-alikansiosta, joka sijaitsee samassa kansiossa kuin sovelluksesi.
Templateissa voit käyttää render_template-kutsussa vietyjen parametrien lisäksi request-, session- ja g-objekteja.
Jinjan koodi erotetaan html-koodista jollakin seuraavista:
- {% ... %} ohjelmakoodi
- {{ ... }} yksittäisen muuttujan tulostaminen html:n sekaan
Kts. Controlling Autoescaping. Muuttujien arvoja voi muokata Jinjan filttereillä. Filtteri ja muuttuja erotetaan toisistaan |-merkillä. Esim. lista|len palauttaa listan pituuden. Jos automatic escaping ei ole päällä, niin jää omalle vastuulle varmistaa, että kaikkien muuttujien sisältö kelpaa html:ään. Tämä voidaan varmistaa e-filtterillä: muuttuja|e. Oletuksena autoescape on päällä kaikissa .html,.htm, .xhtml ja .xml-päätteisissä template-tiedostoissa.
Jinjan for-loopissa voi käyttää apuna loop-muuttujan arvoja. Esim. loop.index antaa 1-alkuisen kierroksen indeksin ja loop.index0 0-alkuisen. Jinja silmukkaa ei voi breakata.
Jos tarvii Jinjassa luoda uusia muuttujia, niin se on tehtävä set-komennolla:
{% set muuttuja = arvo %}
Luentoesimerkit
Uusimmat esimerkit Python3 ja flask:
- Esimerkki 1, lähdekoodi (jinja1-funktio), template : jinja.html
- Esimerkki 2, lähdekoodi (jinja2-funktio), templatet : jinja2.html, base.html
Vanhat CGI-versiot
- jinja.cgi-esimerkki (Lähdekoodi, Jinja template)
- Toinen jinja-esimerkki (Lähdekoodi, Pohjatemplate, Toinen template)
Käyttäjien kommentit