Flask

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

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

stringmerkkijonot ilman /-merkkiä (oletus)
intKokonaisluvut
floatLiukuluvut
pathPolku, 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:

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ä

Kts. Werkzeug Data Structures

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:

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.

Jinja documentation

Jinjan koodi erotetaan html-koodista jollakin seuraavista:

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:

Vanhat CGI-versiot

session

Kts. evästeet ja sessiot

Lisätietoa

Käyttäjien kommentit

Kommentoi Lisää kommentti