Flask ja templatet

Näissä tehtävissä tutustutaan html-koodin tuottamiseen Flaskilla users.jyu.fi-palvelimella. Lisäksi opitaan mitä tarkoittaa HTTP-protokollan tilattomuus.

Tämän tehtävän tekemisen apuna kannattaa käyttää seuraavia dokumentteja:

Autolaskuri

Toteutetaan autolaskuri Flask-ohjelmana

Templatet

Ohjelmakoodin ja HTML-koodin kirjoittaminen sekaisin ei ole hyvä idea vaan aiheuttaa vaikeasti ylläpidettävää ohjelmakoodia ja myös alttius virheille kasvaa. Otetaan käyttöön Jinja2-template, jonka avulla voimme sijoittaa html-koodin omaan tiedostoonsa.

Jinja2-templateen voi kirjoittaa ohjelmakoodia. Syntaksi on melkein kuin pythonia, mutta ei kuitenkaan. Varmista syntaksi aina Jinjan dokumentaatiosta.

Jinjan templatet ja perintä

Jinjan templateilla on helppo tehdä pohjatemplate, joka määrittää kaikille sivuille yhteisen rungon. Lue Template Inheritance. Kokeile tehdä seuraavat templatet.

layout.html, joka toimii kaikkien sivujen yleispohjana

<!doctype html>
<html>
  <head>
    <title>{% block title %}{% endblock %} - Mallisivu</title>
  </head>
  <body>
    <div id="content">{% block content %}{% endblock %}</div>
    <div id="footer">
      {% block footer %}
      TIES4080
      {% endblock %}
    </div>
  </body>
</html>

Muuta jinja.html seuraavanlaiseksi eli käytä edellä luotua layout.html-templatea ja muuta sen sisältöä

{% extends "layout.html" %}
{% block title %}Laskuri{% endblock %}
{% block content %}
Tähän väliin pääsisältö
{% endblock %}

Kokeile miltä sivu näyttää

Millä varmistetaan HTML-rakenteen validius ja ehjyys?

Valitettavasti Jinja ei mitenkään estä kirjoittamasta rikkinäistä ja epävalidia HTML-koodia. On olemassa myös XML-pohjaisia template-kirjastoja (esim. genshi), jotka voisivat olla avuksi, mutta emme nyt käsittele niitä. Jinjan korvikkeena voisi myös käyttää DOM-rajapintaa esim. minidom-kirjaston avulla, mutta kyseinen ratkaisu on raskaampi ja hitaampi kuin Jinja. Jinja ei ole paras ratkaisu XML-datan tuottamiseen, mutta koitamme pärjätä, koska HTML-muodossa jaetut sivut kelpaavat selaimille virheellisinäkin. Lue: HOWTO Avoid Being Called a Bozo When Producing XML.

Sovelluksen kehitysvaiheessa voidaan käyttää apuna XHTML-mediatyyppiä ja selaimessa Javascriptin avulla suoritettavaa validointia. Näitä ei kannata pitää päällä tuotantokäytössä olevassa sovelluksessa. Tämän kurssin tehtävissä nämä voivat olla aina käytössä.

HTML5 ja application/xhtml+xml

HTML5-kielestä on olemassa myös XML-kieliopin mukainen versio, jota monesti kutsutaan nimellä XHTML5. Riittää, että kirjoitat HTML-koodisi XML:n sääntöjen mukaan ja jaat valmiin dokumentin application/xhtml+xml-mediatyypillä. Tämän tyyppisissä dokumenteissa selain ei hyväksy koodia, joka ei ole XML-sääntöjen mukaista. Tätä voi helposti kokeilla esim. users.jyu.fi-palvelimella: kirjoita html-dokumentti, jonka tiedostopääte on .xhtml ja lataa tämä dokumentti Firefox-selaimella. Tee dokumenttiin kirjoitusvirhe esim. unohda jonkun elementin lopetustagi. Sinun pitäisi saada nyt selaimeen virheilmoitus.

XHTML-dokumentissa täytyy nimiavaruusmäärittelyn olla oikein. Kts. seuraava valmis pohja XHTML-dokumentille:

<!DOCTYPE html>
	<html lang="fi" xmlns="http://www.w3.org/1999/xhtml" xml:lang="fi">
		<head>
			<meta charset="utf-8"/>
			<title>Mallipohja</title>
		</head>
		<body>
		</body>
	</html>

Kokeile miten yllämainittu pohja toimii ilman oikeaa nimiavaruusmäärittelyä.

Mahdollinen virheilmoitus näyttää seuraavanlaiselta:

XML Parsing Error: mismatched tag. Expected: </p>.
Location: https://foobar.example/sivu.xhtml
Line Number 45, Column 3:
</body>
--^

Oletuksena Flask käyttää aina text/html-mediatyyppiä eli olettaa kaiken olevan tavallisia www-sivuja. Mediatyypin voi muuttaa seuraavalla tavalla:

    resp = make_response(render_template('template.xhtml'))
    resp.headers['Content-type'] = 'application/xhtml+xml;charset=UTF-8'
    return resp
tai seuraavalla tavalla:
return Response( render_template('template.html', param1=param1), mimetype="application/xhtml+xml")

Kokeile toimiiko aiemmin luomasi sovellus application/xhtml+xml-medityypillä. Korjaa mahdolliset virheet. Jos saat näkyviin pelkän sivun lähdekoodin, muista lisätä templateen nimiavaruusmäärittely.

Automaattinen validointi Javascriptilla

Selain ei mitenkään validoi sille annettua XHTML-dokumenttia vaan ainoastaan tarkistaa, että sen rakenne on XML-sääntöjen mukaista. Validointi pitää tehdä itse. Valmiissa pohjadokumentissa on lisättynä javascriptilla toteutettu HTML-validointi (HTML-inspector), joka suoritetaan aina sivun latauduttua, sekä W3C:n validaattori (suoritetaan CTRL+ALT+V-näppäinyhdistelmällä). Mahdolliset virheet näkyvät selaimen konsolissa.

Muuta aiempi sovelluksesi käyttämään valmista pohjaa ja tutki tuleeko konsoliin virheilmoituksia.

Linkki vs lomake

Flask-WTF

Flask-WTF on laajennettu ja integroitu versio WTForms-kirjastosta, joka helpottaa lomakkeiden käsittelyä.

Käy läpi WTForms crash course. Lue myös Flask-WTF-quickstart, jossa kerrotaan lyhyesti miten tämä eroaa perus WTFormsista.

Arvosanalaskuri

Toteuta edellä olevaa kuvaa vastaava arvosanalaskuri Flask-WTF-kirjaston avulla

FlaskWTF ja CSRF-suojaus (Cross-site request forgery)

Jos saatte lomakkeen virheisiin (form.errors) ilmoituksen:

{'csrf_token': ['The CSRF token is missing.']}

niin teiltä uupuu lomakkeelta FlaskFormin edellyttämä CSRF-suojaus. Lomakkeeseen pitää lisätä:

  {{ form.csrf_token }}

Tähän liittyen voi määritellä oman salaisen avaimen:

# set the secret key.  keep this really secret:
app.secret_key = '"\xf9$T\x88\xefT8[\xf1\xc4Y-r@\t\xec!5d\xf9\xcc\xa2\xaa'

Salaisen avaimen on pysyttävä vakiona ohjelman eri suorituskertojan (latausten) välillä.

Varsinainen CSRF-suojauksen aktivoiminen tehdään seuraavalla tavalla:

from flask_wtf.csrf import CSRFProtect
csrf = CSRFProtect(app)

Jos saat seuraavan virheilmoituksen, ei salainen avaimesi ole pysynyt vakiona ohjelman eri suorituskertojen välillä:

Bad Request

The CSRF session token is missing.

Toinen vaihtoehto on disabloida koko CSRF-suojaus, mutta se ei ole suositeltavaa:

form = PolyglotForm(csrf_enabled=False)

Flask yrittää suojautua hyökkäykseltä lisäämällä jokaiseen lomakkeeseen uniikin tunnisteen. Mahdollinen hyökkääjä ei osaa tätä tehdä.

Jos CSRF-suojaus on käytössä, niin itse suoraan osoitteeseen kirjoitettujen parametrien mukana ei tule tarvittavaa csrf-tokenia. csrf-tarkistuksen voi joissakin tilanteissa ottaa pois päältä myös seuraavalla tavalla:

@app.route('/foo', methods=('GET'))
@csrf.exempt
def foo():
    # ...
    return '...'

CSRF Protection

Lisätietoa: CSRF Protection in Flask

Lisätehtävä: tietojen validointi ilman WTForms-kirjastoa

Arvosanalaskuri

Luo edellä olevaa kuvaa vastaava Flask-sivu. Voit käyttää valmista pohjaa.

Lisää tarkistus jossa varmistetaan, että etunimi ja sukunimi on täytetty ja tiedekunta on valittu. Tee tarkistus vain jos lomake on täytetty eli tarkistusta ei pidä tehdä sivulle ensimmäistä kertaa tultaessa.

  1. Lisää virheilmoitukset yhteen virhesanakirjaan, jossa avaimena käytetään virheellisen kentän nimeä ja arvona virheilmoitustekstiä.
  2. 
        # luodaan dict
        kentat = {"etunimi":"","sukunimi":"","tdk":""}
        errors = dict(kentat) #tekee kopion samoilla avaimilla
        if request.method == 'POST': #varmistetaan, että lomake on lähetetty
            for k in errors:
                try:
                        kentat[k] = request.form[k]
                except KeyError:
                        errors[k] = "!"
    
  3. Muodosta python-sanakirja (dict) tiedekunnista ja vie se templatelle ja luo sen perusteella näkymässä olevan alasvetovalikon sisältö. Käytä avaimena tiedekunnan numeroa.
    
        tiedekunnat = {0:"Valitse tiedekunta", 1:"Humanistinen tiedekunta",2:"Informaatioteknologian tiedekunta", 3:"Kasvatustieteiden tiedekunta", 4:"Liikunta- ja terveystieteiden tiedekunta", 5:"Matemaattis-luonnontieteellinen tiedekunta", 6:"Taloustieteiden tiedekunta", 7:"Yhteiskuntatieteellinen tiedekunta"}
    

    Huomaa, että dictissä olevat avaimet ovat kokonaislukuja ja lomakkeelta saadut tiedot ovat aina merkkijonoja.

  4. Jos tiedoissa oli virheitä, niin tulosta virhe-ilmoitus seuraavilla tavoilla:
    • Ensimmäinen versio: Jos virheitä on niin tulosta yleinen virheilmoitus.
    • Toinen versio: Käy läpi kaikki virhetaulukon virheet ja tulosta ne lomakkeen alussa.
    • Kolmas versio: Tulosta kunkin lomake-elementin viereen siihen liittyvä virheilmoitus.
  5. Aseta kuhunkin lomakkeen kenttään oletusarvoksi edellisellä kerralla syötetty teksti tai valinta
  6. Tekstikentissä, joihin pitäisi syöttää viikkotehtävien pisteet, ei ole vielä name-ominaisuutta asetettu. Mitä name-ominaisuuksiin pitäisi laittaa, että saat kaikki viikkotehtäväpisteet varmasti oikein ja kätevästi käsiteltäväksesi? Huomaathan, että selain voi lähettää lomakkeen kenttien arvot satunnaisessa järjestyksessä.
  7. Tarkista, että viikkotehtäviin on laitettu numero. Tarkista, että numero on väliltä 0-5. Älä kuitenkaan herjaa tyhjästä tekstilaatikosta. Kokeile tehdä tarkistus siten, että käyt yhdessä silmukassa läpi kaikki viikkotehtävien syötteet. Aseta virheellisten pistekenttien taustaväriksi punainen.

Jos vielä jäi jotakin epäselväksi niin voit lisäharjoitella.

Käyttäjien kommentit

Kommentoi Lisää kommentti
Informaatioteknologia - Jyväskylän yliopiston informaatioteknologian tiedekunta