Flask

Flask on mikrosovelluskehys jolla voidaan tehdä web-sovelluksia.

Flask perustuu WerkZeug WSGI-kirjastoon ja Jinja2-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ä. 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. 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!'

# Jos ajetaan suoraan komentoriviltä niin käynnistetään Flaskin oma webbipalvelin
if __name__ == '__main__':
    # asetetaan debug-moodi päälle. Ei saa pitää päällä tuotantokäytössä
    app.debug = True
    app.run()

Flaskia aloitellessa muistakaa users.jyu.fi:ssä kaikki temput mitä vaadittiin, että cgi-ohjelma lähtee toimimaan:

Flask omalle koneelle

Asenna Anaconda (Python 2.7), jonka mukana tulee myös Flask.

Tämän jälkeen riittää, kun käynnistää oman sovelluksen komennolla

python oma.py

ja voi kokeilla selaimella osoitteessa:

http://127.0.0.1:5000/
tai
http://localhost:5000/

Nyt saa myös selkeät virheilmoitukset suoraan selaimeen, koska Flaskin debug-tila toimii omalla koneella.

Halavassa / jalavassa tämä ei onnistu, koska Flaskia ei niistä löydy eikä tule ennen kuin koneet päivitetään samaan käyttisversioon kuin users.jyu.fi. Yliopiston mikroluokkien koneisiin on osaan asennettuna Anaconda.

Lopullinen sovellus pitää yleensä saada toimimaan muualla kuin omalla koneella ja toiminnallisuudessa voi olla eroja esim. tiedostopolut voivat olla erilaisia. Debuggaus on kuitenkin huomattavasti helpompaa omalla koneella. Jos on W:-asema verkkolevynä niin voi suoraan sieltä ajaa sovellustaan oman koneen Flaskilla ja testailla samaan aikaan myös users.jyu.fin kautta.

Debuggaus

Sitten kun flask-sovellus lähtee toimimaan niin virheiden debuggaus on haastavaa, koska users.jyu.fi:ssä ei toimi flaskin oma debug-tila. Ohjelman kaatuessa syntaksivirheeseen tai ajonaikaiseen virheeseen on tuloksena vain internal server error. cgitb-kirjasto ei auta.

Täytyy itse logata virheet. Seuraava tekee lokitiedoston kansioon '../hidden/'. Korjaa polku oikeaksi:

import logging
logging.basicConfig(filename=os.path.abspath('../hidden/flask.log',level=logging.DEBUG)
logging.debug("Tänne mitä haluaa lokiin kirjoittaa")

Älä kirjoita lokia cgi-bin-kansioon tai sen alikansioihin!. Tämä ei toimi.

Jotta lokista olisi iloa on käytettävä try..exceptiä. Nappaa exceptillä kiinni mahdolliset virheet ja dumppaa virheilmoitus lokiin:

try:
       # sqlite haluaa absoluuttisen polun
       con = sqlite3.connect(os.path.abspath('../hidden/resepti'))
except Exception as e:
       logging.debug("Kanta ei aukea")
       # sqliten antama virheilmoitus:
       logging.debug(str(e))

Lokin sisältöä voitte seurailla helpoiten ottamalla pääteyhteyden halavaan ja käyttämällä tail-komentoa:

tail -f flask.log

Tämä kyttää koko ajan tiedoston sisältöä ja näyttää heti sinne tulevat uudet rivit.

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

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.

Templatet

Flask käyttää Jinja2-templateja aivan samaan tapaan kuin aiemmin on opittu CGI-ohjelman yhteydessä.

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.

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, joka tulee jos pyydettyä parametriä ei löydykkään:

@app.route('/login', methods=['POST', 'GET'])
def login():
    if request.method == 'POST':
        try:
            if valid_login(request.form['username'],
                           request.form['password']):
                return log_the_user_in(request.form['username'])
            else:
                error = 'Invalid username/password'
        except KeyError:
                error = 'Invalid form'
            
    # the code below is executed if the request method
    # was GET or the credentials were invalid
    return render_template('login.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", "")
    # 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 muodostettu response-objekti pitää erikseen ottaa talteen, muokata ja vasta sitten palauttaa:

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 BaseResponse

session

Kts. evästeet ja sessiot

Lisätietoa

Käyttäjien kommentit

Kommentoi Lisää kommentti
Kurssimateriaalien käyttäminen kaupallisiin tarkoituksiin tai opetusmateriaalina ilman lupaa on ehdottomasti kielletty!
http://appro.mit.jyu.fi/web-sovellukset/luennot/flask/
© Tommi Lahtonen (tommi.j.lahtonen@jyu.fi) <http://hazor.iki.fi/>
2016-03-08 16:11:06
Informaatioteknologia - Jyväskylän yliopiston informaatioteknologian tiedekunta