Web2py ja Google App Engine
Tutustutaan web2py-frameworkin ja Google App Enginen perusteisiin. Käytä apunasi web2py-kirjaa ja App Enginen ohjeita.
Web2py ja Google App Engine käyttöönotto
Valmis pääteohjaus toimivana sovelluksena
valmis pääteohjaus zip-paketoituna
- Asenna koneellesi Python 2.7 (https://www.python.org/ftp/python/2.7.13/python-2.7.13.msi ). Älä asenna uudempaa Pythonin versiota. Jos olet jo asentanut Anacondan niin sen pitäisi riittää. Agoran mikroluokkien koneille Python 2.7 on jo asennettu.
- Asenna koneellesi Google App Engine SDK for Python https://cloud.google.com/appengine/docs/standard/python/download. Älä asenna Googlen sivun ehdottamaa Google Cloud SDK:ta vaan sivulta alempaa kohdasta Optional: Download and install the original App Engine SDK for Python löytyvä App Engine SDK for Python. Agoran mikroluokkiin App Engine SDK on jo asennettu. Jos haluat käyttää App Enginea Linuxissa niin vilkaise ohje: Installing and Running Google App Engine on a Linux System
- Kopioi itsellesi web2py lähdekoodiversio osoitteesta http://www.web2py.com/examples/static/web2py_src.zip
- Pura web2py.zip-paketti ja tee kaikki seuraavat muutokset paketissa olevaan web2py-kansioon ja sen alikansioihin.
-
Google App Engine launcherilla suoritettaessa web2py-sovellusta on web2py-kansiorakenteen juureen lisättävä appengine_config.py-niminen tiedosto josta löytyvät seuraavat rivit:
import sys sys.platform = 'linux3'
- Nimeä web2py/applications/welcome-kansio haluamaksesi (sovelluksesi nimi)
- Kopioi web2py/examples-kansiosta routes.parametric.example.py suoraan web2py-kansion juureen ja muuta tiedoston nimeksi routes.py. Muuta routes.py-tiedoston sisältä:
routers = dict( # base router BASE=dict( default_application='oman kansion nimi', ), )vastaamaan omaa kansiotasi. Jos myöhemmin muutat routes.py-tiedoston sisältöä niin www-palvelin (App Engine Launcher) on käynnistettävä uudelleen jotta muutokset tulevat voimaan. Voi myös kokeilla admin-liittymän Reload routes-painiketta osoitteessa http://localhost:8080/admin/site. - Kopioi web2py/examples-kansiosta app.example.yaml-tiedosto suoraan web2py-kansion juureen ja muuta tiedoston nimeksi app.yaml
- Muuta seuraava kohta app.yaml-tiedostoon. Keksi ohjelmallesi jokin oma
uniikki tunniste esim. kayttajatunnus-1. Tunniste ei saa alkaa numerolla eikä siinä saa olla välilyöntejä.
application: tunnus-1 version: 1 api_version: 1
- Kopioi web2py/handlers-kansiosta gaehandler.py suoraan web2py-kansion juureen
- Käynnistä Google App Engine Launcher.
Jos App Enginea ei löydy Start-valikosta niin kokeile käynnistämistä suoraan polusta: C:\Program Files (x86)\Google\google_appengine\launcher\GoogleAppEngineLauncher.exe. Älä käytä appengine_launcher.bat:ia.
Jos App Engine Launcher valittaa, että ei löydä python-tulkkia niin lisää oikea polku komentotulkkiin (python.exe) App Engine Launcherin asetuksiin Edit|Preferences|Python Path -kohtaan.
Linux-ympäristössä ei ole erillistä GUI-sovellusta vaan käynnistäminen pitää tehdä komentoriviltä. Siirry kansioon johon purit googlelta hakemasi paketin ja suorita sieltä dev_appserver.py-ohjelma:
./dev_appserver.py /polku/oman/sovelluksen/kansioon/omasovellus
- Lisää Launcheriin edellä luomasi sovelluksen web2py-kansio valinnalla File|Add Existing Application

- Valitse sovelluksesi Launcherin listasta ja käynnistä sovellus Run-painikkeella
- Kokeile Browse-painikkeella toimiiko sovelluksesi. Selaimeen pitäisi aueta sivu, jonka osoite on muotoa
http://localhost:8080/welcome/default/index ja jossa lukee hello.
Huomaa, että osoitteessa näkyvä portti (8080) on todennäköisesti jokaisella koneella eri ja pitää siis korvata omalla koneella pyörivän App Engine Launcherin tarjoamalla porttinumerolla.
- Jos sovellus ei toimi niin kokeile Launcherin Logs-painiketta niin näet mitä viestejä virhelokiin on tullut
- Jos sovellus ei suostu käynnistymään (Browse-painike ei aktivoidu) niin varmista, että web2py-sovelluksesi hakemistopolussa ei esiinny erikoismerkkejä, skandinaavisia merkkejä eikä välilyöntejä. Varmista myös, että omassa kotihakemistossasi ei ole skandinaavisia merkkejä. Esim. polku c:\users\käyttäjä\ hajottaa launcherin.
Jos sovellus ei suostu toimimaan App Engine Launcherilla niin sen voi ajaa myös web2pyn omalla palvelimella.
- Sulje App Engine Launcher
- Avaa komentokehote ja siirry kansioon jossa web2py-sovelluksesi sijaitsee
- Käynnistä web2py-palvelin:
python web2py.py
- Keksi jokin admin-salasana ja valitse Start Server
- Kokeile myös miltä App Enginen admin-näyttää osoitteessa http://localhost:8000/instances Tarkista oikea portti App Engine Launcherin ikkunasta.
- Sovelluksen testaaminen ja kehittäminen on hyvä tehdä App Engine Launcherin avulla omalla koneella. Valmis sovellus
pitää kuitenkin julkaista Googlen palvelimella:
- Luo itsellesi uusi sovellus App Enginen sivulla
Anna projektille sama id kuin aiemmin keksit!

- Käytä aiemmin app.yaml-tiedostoon kirjoittamaasi tunnistetta (Project ID)
- Keksi sovellukselle myös nimi (Project Name)
- Mene appenginen ylläpitosivulle. Jos google ehdottaa ensimmäisen sovelluksen luomista
niin tee tämä googlen ohjeiden mukaisesti. Valitse sovelluksen kieleksi Python ja sovelluksen sijoituspaikaksi europe-west. Jos haluat niin voit käydä
läpi koko googlen tarjoaman tutoriaalin mutta se ei ole välttämätöntä.
Managing Cloud Platform Projects, App Engine Applications, and Billing .
- Julkaise sovelluksesi App Launcherin Deploy-painikkeella. App Launcher pyytää google-tunnuksesi ja salasanasi ja avaa selaimeen kirjautumissivun. Valitse Allow
Jos kaikki menee kuten Strömsössä niin luvan jälkeen App Engine Launcher päivittää sovelluksesi Googlen palvelimelle.
Jos mitään ei kuitenkaan tapahtu niin avaa tästä vaihtoehtoinen ohje
- Katso App Engine Launcherin lokiin tullut viesti:
Your browser has been opened to visit: https://accounts.google.com/o/oauth2/auth?scope=https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fappengine.admin+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fcloud-platform+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.email&redirect_uri=http%3A%2F%2Flocalhost%3A8080%2F&response_type=code&client_id=550516889912.apps.googleusercontent.com&access_type=offline If your browser is on a different machine then exit and re-run this application with the command-line parameter --noauth_local_webserver - Avaa Command Prompt, siirry kansioon, jonka alla on sovelluksesi kansio, ja käynnistä komentoriviltä appcfg.py-ohjelma:
"C:\Program Files (x86)\Google\google_appengine\appcfg.py" --noauth_local_webserver update u:\polku\sovelluksen_kansio
- Ohjelma pyytää käymään seuraavanlaisessa osoitteessa:
Go to the following link in your browser: https://accounts.google.com/o/oauth2/auth?scope=https%3A%2F%2Fwww.googleapis .com%2Fauth%2Fappengine.admin+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fcloud-pl atform+https%3A%2F%2Fwww.googleapis.com%2Fauth%2Fuserinfo.email&redirect_uri=urn %3Aietf%3Awg%3Aoauth%3A2.0%3Aoob&response_type=code&client_id=550516889912.apps. googleusercontent.com&access_type=offline Enter verification code:Käy sivulla, valitse Allow ja kopioi talteen saamasi koodi:

ja liitä koodi command promptissa odottavalle ohjelmalle:
Enter verification code: 4/Ex34XvQlw1NZjin6xxKy_r91FnNNTPndbHzoy6QI39w
Jatkossa tätä tunnistautumista ei tarvitse enää tehdä.
- Katso App Engine Launcherin lokiin tullut viesti:
- Kokeile sovellustasi osoitteessa http://oma_tunniste.appspot.com
- Sovelluksesi resursseja ja muita tietoja voit silmäillä App Enginen ylläpitosivun kautta. Esim. jos sovelluksesi kaatuu niin voit ylläpitosivun kautta etsiä Error Reporting-sivulta kaatumisen aiheuttaneen virheen. Sovelluksen päivittäminen pilveen ei kopioi mukana lokaalin app engine launcherin tietokannan sisältöä.
- Luo itsellesi uusi sovellus App Enginen sivulla
Anna projektille sama id kuin aiemmin keksit!
- Kokeile nyt myös ylläpitoliittymää osoitteessa https://oma_tunniste.appspot.com/admin/. Todennäköisesti admin-liittymä ei toimi mutta jos toimii niin joudut kirjautumaan sisään google-tunnuksella. Tutki mitä kaikkea löytyy wep2pyn ylläpitoliittymästä. Kokeile myös tietokannan ylläpitosovellusta https://oma_tunniste.appspot.com/appadmin/. Kokeile samaa sivua mutta omalla koneellasi toimivan palvelimen eli App Launcherin kautta osoitteessa: http://localhost:8080/admin/. Voit myös kokeilla tietokannan ylläpitosovellusta osoitteessa http://localhost:8080/appadmin.
- Tutki mitä löytyy applications/omasovellus/Controllers-kansion default.py-tiedostosta. Tiedostossa oleva index-funktio suoritetaan sovelluksen sivulle tultaessa. T-funktio hoitaa sisällön kääntämisen eri kielille. Muuta funktion asettamia ja palauttamia tietoja ja kokeile miten www-sivun sisältö muuttuu
- Tutki mitä löytyy applications/omasovellus/views/default/index.html-tiedostosta. Tämä on index-funktiota vastaava näkymä (template) ja vastaa varsinaisen HTML-koodin tuottamisesta. Muuta tiedoston sisältöä ja testaa miten www-sivu muuttuu.
- Lisää default.py-tiedostoon uusi kurssi-funktio:
def kurssi(): response.flash = "Kokeilen web2py-juttuja" return dict(testi='Hyvä') - Kokeile osoitetta http://localhost:8080/kurssi. Sivun pitäisi käyttää oletusnäkymää (generic.html). Oletusnäkymä toimii vain omalla koneella testattaessa eli jos julkaiset sovelluksen Googlen palvelimille niin tämä sivu ei toimisi.
- Luo views/default-kansioon uusi tiedosto kurssi.html ja kirjoita tiedoston sisällöksi:
<!DOCTYPE HTML> <html> <head> <title>web2py-kokeilu</title> </head> <body> <p>{{=testi}}</p> </body> </html> - Kokeile nyt uudelleen osoitetta http://localhost:8080/kurssi. Sivun sisällön pitäisi muuttua.
- Tutki mitä applications/omasovellus/models-kansion tiedostot sisältävät. Luo models-kansioon uusi tiedosto omat.py ja kirjoita siihen:
# oman tietokannan määrittelyt käyttäen web2pyn DAL-rajapintaa db.define_table('kurssi', Field('nimi', 'string', required=True, length=256, label="Kurssin nimi"), Field('koodi', 'string', required=True, length=8, label="Kurssikoodi"))
Huom! Älä vahingossa käytä text-tietotyyppiä. text-tietotyyppi soveltuu vain suurien ja ei indeksoitavien tekstimäärien tallentamiseen. Normaalisti pitää käyttää string-tyyppiä.
-
Mene tutkimaan mitä löytyy web2pyn tietokanta-appadmin-sivulta osoitteesta http://localhost:8080/appadmin/index. Taululistauksessa
pitäisi näkyä yllä määritelty kurssi-taulu. Yritä lisätä admin-liittymän kautta muutama rivi kurssitauluun.
Voit kokeilla myös laajempaa Admin-sovellusta osoitteessa http://localhost:8080/admin (sorry, ei toimi tällä hetkellä). Jos et pääse Adminiin vaan saat virheilmoituksen Admin is disabled because insecure channel niin varmista, että käyttämäsi osoite on localhost-alkuinen.
- Muuta kurssi-funktio seuraavanlaiseksi:
def kurssi(): response.flash = "Kurssit" # haetaan tietokannasta kaikki kurssit kurssit = db().select(db.kurssi.ALL) return dict(testi='Hyvä', kurssit=kurssit)Täytyy lisätä myös vastaava näkymä (view, vrt. Jinjan templatet) eli luo tiedosto views/default/kurssi.html ja kirjoita sinne seuraavanlainen sisältö. Nyt tarvitaan silmukka, joka osaa tulostaa kaikki kurssit. Huomaa pass-avainsana, joka kertoo missä vaiheessa silmukkakoodi loppuu. Pythonin normaali sisennyskäytäntö ei toimi näkymissä. Web2pyn näkymät ymmärtävät kaikki tavalliset python-kielen rakenteet. Lisätietoa: web2py views.<!DOCTYPE HTML> <html> <head> <title>web2py-kokeilu</title> </head> <body> <p>{{=testi}}</p> <h2>Listaus</h2> <ul> {{for k in kurssit:}} <li>{{=k.koodi}} {{=k.nimi}}</li> {{pass}} </ul> </body> </html> - Lisätään sivulle vielä lomake (SQLFORM) jolla voi lisätä uusia kursseja. SQLFORM-luokka hoitaa automaattisesti lomakkeen muodostamisen ja sen käsittelyn.
Muuta palaute-funktio seuraavanlaiseksi:
def kurssi(): response.flash = "Kurssit" kurssit = db().select(db.kurssi.ALL) form = SQLFORM(db.kurssi) if form.process().accepted: response.flash = 'Kurssi on lisätty' elif form.errors: response.flash = 'Lomakkeen tiedoissa on virheitä' else: response.flash = 'Täytä lomake lisätäksesi uuden palautteen' return dict(testi='Hyvä', kurssit=kurssit, lomake=form) - Lisää kurssi.html-näkymään rivi:
{{=lomake}} - Kokeile lisätä uusi kurssi lomakkeella. Toimiiko? Ilmestyykö uusi kurssi listaan? Kokeile ladata sivu uudelleen hetken kuluttua. Hajautettu tietokanta tekee tepposia ja lisäys ei näy samantien...
- Muutetaan sivua vielä sen verran, että voidaan myös muokata kursseja. Muuta näkymän listauskoodia:
<li><a href="?id={{=k.id}}">{{=k.koodi}} {{=k.nimi}}</a></li>Kokeile miten sivu muuttuu. - Käsittelijää pitää vielä muokata huomaamaan mahdollisesti valittu kurssi. Querystringin käsittely
tapahtuu aivan samaan tapaan kuin Flaskissa.
Muuta käsittelijässä lomakkeen luominen ( form = SQLFORM(db.kurssi) )
seuraavaksi:
if request.get_vars['id'] != None: kurssi = db.kurssi( request.get_vars['id'] ) form = SQLFORM(db.kurssi, kurssi, deletable=True) else: form = SQLFORM(db.kurssi)
Tutustu web2pyn request-objektiin
- Jos lomakkeella näkyy id-kenttä niin sen saa piilotettua lisäämällä SQLFORMille parametri showid=False:
form = SQLFORM(db.kurssi, showid=False)
- Kokeile nyt sivua. Onnistuuko kurssin muokkaaminen? Muista hajautetun tietokannan aiheuttama viive.
- Tee ohjelmakoodiin kirjoitusvirhe ja kokeile miten sivu toimii. Sinun pitäisi saada virheilmoitus ja linkki tikettiin. App Engine -ympäristössä linkkiä ei voi käyttää. Katso App Engine Launcherin Logs-painikkeella tarkka virheilmoitus. Käy tutkimassa admin-sivulta sovelluksen virheitä (ei toimi...).
- Lisää tietokannan rakenteeseen toinenkin taulu, jolla voi lisätä kurssille palautteita ja arvosanoja. Nyt halutaan myös viitata (reference) edellä määriteltyyn tauluun.
Rakenteen määrittelemisen yhteydessä voidaan myös määrätä millaista widgetia käytetään lomakkeella
ja miten alasvetovalikossa esitetään kurssin valinta.
db.define_table('palaute', Field('kurssi', 'reference kurssi', required=True), Field('palaute', 'string', required=False, length="4096", label="Kirjoita palaute", widget=SQLFORM.widgets.text.widget), Field('arvosana', 'integer', required=True, default=0, requires=IS_IN_SET([0, 1, 2, 3, 4, 5]), widget=SQLFORM.widgets.radio.widget, label="Arvosana")) # kenttien ominaisuuksia voi määritellä myös muualla kuin suoraan konstruktorissa # määritellään viitekentän esitysmuoto ja mistä taulusta haetaan vaihtoehdot db.palaute.kurssi.requires = IS_IN_DB(db, db.kurssi.id, '%(koodi)s %(nimi)s')Kentän requires-ominaisuutta voi joutua säätämään myös lennosta ohjelmakoodissa:
db.taulu.kentta.requires = IS_IN_SET({{"1":"Omena","2","Appelsiini", "3":"Banaani"}}) hetelmat = dict() #... haetaan tarjolla olevat hedelmät ja sijoitetaan ne dictiin ... db.taulu.kentta.requires = IS_IN_SET(hetelmat) - Luo uutta taulua varten oma sivu (palaute.html) ja controller-funktio (palaute). Mahdollista sivulla uusien palautteiden syöttäminen. Listaa myös jo olemassaolevat palautteet sivulle ja mahdollista niiden muokkaaminen ja poistaminen
- Kokeile palautelistauksessa miten näkymässä toimii palaute.kurssi.nimi
- Lisää palaute-tauluun jokin itse keksitty kenttä, jonka tyyppi on list:string.
Kokeile miten lomake muuttuu ja toimii. list:string-kentän toiminta edellyttää web2pyn
oman sivupohjan (layout.html) käyttämistä, koska siinä käytetään javascriptia. Tee palaute-funktiosta kopio palaute2-nimelle ja kokeile tätä sivua ilman omaa view-tiedostoa, jolloin se käyttää web2pyn valmista pohjaa.
Entäpäs miten toimivat tyypit list:integer ja json? Taulussa voi myös olla kenttiä joita ei haluta näytettävän lomakkeella vaan ne täytetään ohjelmallisesti. Kokeile piilottaa jokin kenttä ja aseta sen arvo ohjelmakoodissa:
db.taulu.kentta.writable = False db.taulu.kentta.readable = False
Kentän sisällön voi nyt helposti asettaa koodissa:
form = SQLFORM(db.taulu) form.vars.kentta = "arvo"
Kyselyt
Web2py käyttää kyselyissä omaa syntaksiaan, joka etäisesti muistuttaa SQL-kieltä. Kokeile toteuttaa seuraavat:
- Tee sivu, joka listaa kaikki vain tietyn kurssin palautteet
palautteet = db(db.palaute.kurssi==tähän kurssin id).select()
- belongs-funktio vastaa SQL-kielen IN-operaattoria. Sen avulla voi testata
kuuluuko jokin arvo joukkoon. Esim.
jutut = db(db.taulu.kentta.belongs([1,2,3,4,5])).select() lista = ["foo","bar","foobar"] jutut2 = db(db.taulu.kentta2.belongs(lista)).select()
Tee sivu, joka listaa kaikki palautteet joiden arvosana on 1,2 tai 3.
Sessiot
- Testataan vielä sessioiden toimintaa ja laitetaan sivulle pieni laskuri. Lisää käsittelijään seuraavat rivit
ja palauta session.laskuri-muuttuja näkymälle:
if not session.counter: session.counter = 1 else: session.counter += 1 - Lisää näkymään tarvittava koodi, että saat laskurin näkyviin. Kokeile kasvaako laskuri sivua uudelleen ladattaessa.
Pohjatemplate
- Otetaan vielä näkymässä käyttöön pohjanäkymä josta olisi hyötyä jos sovelluksessa olisi useita sivuja ja halutaan
pitää ne samannäköisinä. Lisää views-kansioon, eli samaan kansioon missä on myös generic.html, uusi tiedosto pohja.html, jonka sisältö on seuraava:
<!DOCTYPE HTML> <html> <head> <title>{{=response.title}}</title> </head> <body> <p>Tämä teksti tulee yhteisestä pohjasta</p> {{include}} <p>Tämä teksti tulee yhteisestä pohjasta</p> </body> </html> - Poista nyt kurssi.html-tiedostosta kaikki se mitä pohja.html-tiedostossa jo lukee ja kirjoita tiedoston alkuun rivi:
{{extend 'pohja.html'}} - Kokeile miten sivun ulkoasu muuttui
Lue Blocks in views
Edelleenohjaus
Edelleenohjaus onnistuu web2py-ympäristössä yhtä helposti kuin flaskissakin:
redirect("http://appro.mit.jyu.fi/tiea2080/")
Jos tarvitset jonkun olemassa olevan funktion osoitteen niin sen saa URL-funktiolla:
redirect(URL('index'))
Autentikointi
- Lisää sivustolle autentikointi. Lisää models-kansion omat.py-tiedoston alkuun rivit:
from gluon.contrib.login_methods.gae_google_account import GaeGoogleAccount auth.settings.login_form = GaeGoogleAccount()
Lisää käsittelijään uusi funktio (ellei siellä jo ole):def user(): return dict(form=auth())ja viimeiseksi lisää autentikointia vaativien funktioiden eteen rivi @auth.requires_login(). Esimerkiksi:@auth.requires_login() def kurssi(): ...
- Poista web developer toolbarin avulla kaikki domain-cookiet (poistaa mahdollisen kirjautumisen ja session) ja kokeile sen jälkeen mennä palautesivulle. Sovelluksen pitäisi pyytää kirjautumaan Google-tunnuksella.
- Jos tarvitset kirjautuneen käyttäjän tunnistetta niin se löytyy helposti: auth.user.id. Tarkemmat tiedot kirjautuneesta käyttäjästä löytyvät tietokannan auth_user-taulusta.
JSON ja XML
Web2py tuottaa automaattisesti json- ja xml-versiot sivuista, jos lisäät seuraavan rivin models/oma.py-tiedostoon:
response.generic_patterns = ['*']
Kokeile nyt aiemmin kokeilemiasi sivuja, mutta lisää osoitteen perään .json tai .xml. Esim.
http://localhost:8080/palaute.jsontai
http://localhost:8080/palaute.xml
Ajax-sovellus olisi helppo rakentaa näiden avulla...
Debuggaus
Web2py-sovellusten debuggaamiseen on monia vaihtoehtoja ja tasoja:
- Aseta näkymässä näkyville debuggaus-palkki:
{{=response.toolbar()}} - Tutki Google App Engine Launcher virhelogia (Logs-painike)
- Tutki App Enginen Error Reporting -sivua.
- Tutki Admin-sivun Errors-listausta (ei toimi)
- Käytä web2pyn omaa www-palvelinta ja sen debuggausominaisuuksia
- Käytä Eclipseä ja PyDev:iä

Käyttäjien kommentit