Google App Engine
Siirretään sovelluksemme Googlen App Engine -ympäristöön.
- Asenna koneellesi Python 2.7 (https://www.python.org/ftp/python/2.7.16/python-2.7.16.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://storage.googleapis.com/appengine-sdks/featured/GoogleAppEngine-1.9.84.msi. Agoran mikroluokkiin App Engine SDK on jo asennettu. Jos haluat käyttää App Enginea Linuxissa niin lue ohje: Installing Cloud SDK for Python
- Kopioi itsellesi valmiin projektin pohja. Valmiissa pohjassa on lib-kansiossa olevaan virtuaaliympäristöön asennettuna kaikki olennaisimmat kirjastot. Pura pohja haluamaasi kansioon.
- Muuta paketissa olevaan app.yaml-tiedostoon itse keksitty tunniste application-riville:
application: omauniikkiprojektiid
Tunnistetta ei voi myöhemmin muuttaa. Käytä esim. omatunnus-tiea2080-ohjaus6. Käytä tunnisteessa vain pieniä kirjaimia ja numeroita. - Javascript-tiedostot, css-tiedostot ja muut vastaavat täytyy sijoittaa static-kansioon.
- 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ä purkamasi pohjan kansio valinnalla File|Add Existing Application

- Valitse sovelluksesi Launcherin listasta ja käynnistä sovellus Run-painikkeella

- Kokeile Browse-painikkeella toimiiko valmiissa pohjassa oleva hello world -sovellus. Selaimeen pitäisi aueta sivu, jonka osoite on muotoa
http://localhost:8080/ 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ä 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ä\ voi hajoittaa launcherin.
- Jos sovellus ei toimi niin kokeile Launcherin Logs-painiketta, niin näet mitä viestejä virhelokiin on tullut
- Kokeile myös miltä App Enginen SDK Cosole 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:
- Ota käyttöön Googlen tarjoama tarjouskuponki, jolla voit kokeilla Googlen maksullisia pilvipalveluja. App enginen peruskäyttö on ihan ilmaista, mutta esim. SQL-tietokannan käyttäminen maksaa.
- Luo uusi App Engine projekti

-
Käytä projektin nimenä ja id:nä samaa tunnistetta, kuin käytit aiemmin app.yaml-tiedostossa.
Valitse Billing Accountiksi edellä aktivoimasi kuponki. Jos Billing Account -valintaa ei näy ollenkaan niin älä huolestu. Google valitsee automaattisesti ainoan mahdollisen eli edellä luodun kupongin.
Valitse organisaatioksi student.jyu.fi tai No Organization.
Google muuttaa käyttöliittymää vähän väliä, mutta älä anna sen hämätä.
Lisätietoa: Managing Cloud Platform Projects, App Engine Applications, and Billing .
-
Jos mahdollista, niin valitse projektin sijoituspaikaksi europe-west

- Jos mahdollista, niin valitse sovelluksen kieleksi Python ja ympäristöksi Standard
- Julkaise sovelluksesi App Engine Launcherin Deploy-painikkeella. App Engine 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 tapahdu, niin lue 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öä.
- Lisätietoa löytyy Googlen ohjeesta Getting Started with Flask on App Engine Standard Environment
Google Datastore
Googlen Datastore on NoSQL-dokumenttitietokanta. Datastorea voi käyttää melkein samaan tapaan kuin relaatiotietokantoja.
Limitations: The Datastore enforces some restrictions on queries. Violating these will cause it to raise exceptions. For example, combining too many filters, using inequalities for multiple properties, or combining an inequality with a sort order on a different property are all currently disallowed. Also filters referencing multiple properties sometimes require secondary indexes to be configured.
Unsupported: The Datastore does not directly support substring matches, case-insensitive matches, or so-called full-text search. There are ways to implement case-insensitive matches and even full-text search using computed properties.
Liitoksia ei voi käyttää Datastoren kyselyissä.
Entity Models
Ensimmäiseksi tarvitsee luoda malliluokat, jotka kuvaavat tietokantaan tallennettavaa tietoa. Vrt. SQL-tietokannan CREATE TABLE -lauseet.
Mallipohjassa luodaan ja alustetaan ohjaus 3:ssa käytetty reseptitietokanta. Katso main.py-tiedostosta mallikoodi. Kokeile suorittaa populate-sivu, joka tallentaa datastoreen reseptitietokannan sisällön. Huom. Jos suoritat sivun useampaan kertaan, niin myös sisältö tallentuu useamman kerran.
SDK Consolen Datastore Viewerillä voit katsoa mitä datastoreen tallentuu. Voit myös muokata datastoren sisältöä. Huom! Kaikki testaaminen on syytä tehdä lokaalissa ympäristössä App Engine Launcherilla. Jos muokkaat datastorea suoraan Googlen pilvipalvelussa, niin jokainen muutos- ja lukuoperaatio maksaa. Kts. Always Free usage limits.
Tutustu seuraaviin dokumentteihin:
Kyselyt
- Lisää main.py-tiedostoon uusi sivu /reseptit/, jolla listaat kaikki datastoreen syötetyt reseptit
- Yksinkertaisin kysely tehdään seuraavasti:
reseptit = Resepti.query().fetch()
Tutustu NDB Queries-ohjeeseen. Koita järjestää reseptit nimen mukaan aakkosjärjestykseen. Tee sopiva template ja kokeile saada sivu toimimaan. - Kun reseptilistaus toimii lokaalisti App Engine Launcherilla, niin julkaise (deploy) sovelluksesi pilvipalvelussa ja kokeile miten se toimii siellä.
- Lokaalin datastoren sisältö ei kopioidu pilveen eli joudut ajamaan populate-funktion ensin
- Jos sovellus kaatuu Googlen pilvipalvelussa niin katso mitä Error Reporting-sivu kertoo.

-
Mahdollisesti sovelluksesi
indeksejä ei ole vielä ehditty luoda. Katso myös mitä Datastore indexes-sivulla tapahtuu. Ensimmäisellä
käyttökerralla indeksien luominen voi kestää useita minuutteja.
Kaikki sovelluksen sivut eli käytännössä kaikki erilaiset kyselyt täytyy suorittaa lokaalilla sovelluksella ennen kuin niitä voi siirtää pilvipalveluun. App Engine Launcher generoi tarvittavat indeksit automaattisesti käytön mukaan. Näiden perusteella vastaavat indeksit luodaan myös varsinaisessa
pilvipalvelusssa. Lue Cloud Datastore Indexes.

- Hae myös kaikki ruokalajit ja listaa ne myös samalla reseptisivulla
- Listaa reseptisivulla reseptien nimien yhteydessä myös sen ruokalajin nimi johon resepti kuuluu. Et pysty hakemaan näitä tietoja yhdellä kyselyllä, kuten relaatiotietokannan yhteydessä. Sinun pitää erikseen yhdistellä nämä tiedot. Esim. Lisää kaikki ruokalajit yhteen dictionaryyn, jossa käytät avaimena kunkin ruokalajin id:tä. Id:n saat ruokalajin keyltä: ruokalaji.key.id(). Kts. Key. Vastaava key on tallennettu myös jokaisen reseptin ruokalaji-ominaisuuteen. Key yksilöi jokaisen datastoreen tallennetun objektin. Jonkun tietyn objektin voi aina pyytää datastoresta Keyn avulla (key.get()).
WTForms-Appengine
- Luo lomake ruokalajien editoimista varten. Käytä apuna WTForms-Appengine-luokkaa, joka on WTFormsista Appenginea ja datastorea varten räätälöity versio. Voit suoraan
luoda lomakeluokan Datastoren tietorakenneluokan pohjalta model_form-funktiolla. Tässä kannattaa vielä käyttää pohjaluokkana (base_class)
FlaskForm-luokkaa niin saa Flask-tuen samalla:
from wtforms_appengine.ndb import model_form from wtforms_appengine.fields import KeyPropertyField from flask_wtf import FlaskForm from wtforms import SelectField, HiddenField RuokalajitForm = model_form(Ruokalaji, base_class=FlaskForm)
- Kokeile miten RuokalajitForm toimii. Yritä lisätä uusia ruokalajeja. Uuden ruokalajin voit luoda, kun vain luot uuden esiintymän Ruokalaji-luokasta ja asetat sen propertyihin halutut tiedot (lomakkeelle syötetyt) ja tallennat datastoreen put()-metodilla.
- Jos haluat lisätä tarkistuksia lomakkeelle, niin peri uusi lomakeluokka RuokalajitForm-luokasta. Voit uudelleenmääritellä kenttiä ja asettaa niille omia tarkistuksia ja rajoitteita.
- Luo samaan tapaan lomake myös reseptien muokkaamista varten. Miten lomake toimii? Miten ruokalajin valitseminen toimii?
- Ruokalajin käsitteleminen vaatii hieman lisätemppuja. WTForms ei automaattisesti tiedä millä tavalla
ruokalaji pitäisi esittää alasvetovalikossa. Lue KeyPropertyField-tyyppisen kentän dokumentaatio: WTForms-Appengine NDB.
Kokeile seuraavaa:
#luodaan luokka tietomallin pohjalta ReseptitBaseForm = model_form(Resepti, base_class=FlaskForm) # Palauttaa tekstin, joka näytetään select-kentässä, josta valitaan ruokalaji def ruokalaji_label(ruokalaji): return ruokalaji.nimi # Korjaillaan suoraan tietorakenteesta saatua formia. Lisätään ruokalaji-kentälle # oma labeli class ReseptitForm(ReseptitBaseForm): ruokalaji = KeyPropertyField(reference_class=Ruokalaji, get_label=ruokalaji_label) - Yritä lisätä uusia reseptejä datastoreen.
- Tee reseptilistauksesta linkkilista, jolla voit valita jonkun resepteistä lomakkeelle muokattavaksi.
Käytä linkissä reseptin tunnisteena key.urlsafe()-metodin palauttamaa merkkijonoa. Tämän merkkijonon avulla voit aina luoda saman keyn uudelleen. Kts. NDB Key Class:
key = ndb.Key(urlsafe=urlsafella_aiemmin_luotu_merkkijono) #haetaan avainta vastaava tietue datastoresta: tietue = key.get()
Käytä muotoa /reseptit/tähäntuleeurlsafe-olevia linkkejä
- Vastaavasti muuta funktiosi route muotoon (Kts. Variable rules):
@app.route('/reseptit/<resepti_id>', methods=['GET','POST']) def reseptit(resepti_id): - Voit nyt helposti luoda lomakkeen, jolla on valmiiksi editoitavana valittu resepti:
try: resepti = ndb.Key(urlsafe=resepti_id).get() # ReseptitForm(obj=resepti) alustaa lomakkeen resepti-objektilla, jos lomaketta ei ole vielä lähetetty formReseptit = ReseptitForm(obj=resepti) except: formReseptit = ReseptitForm() - Reseptin varsinainen lisääminen kannattaa tehdä omassa funktiossaan, josta ohjataan lisäämisen
jälkeen reseptisivulle. Jos lisääminen onnistuu niin siirrytään edelleenohjauksella (redirect), jos
taas lomakkeessa on jotain vikaa niin tehdään normaalia funktiokutsu. Edelleenohjatessa lomakkeelta lähetetyt tiedot (POST) eivät siirry mukana, mutta jos tehdään normaali funktiokutsu niin ne lomaketiedot
ovat mukana. Muuta reseptilistaussivun lomake sellaiseksi, että se osaa listata virheilmoitukset.
@app.route('/tallenna_resepti', methods=['POST']) def tallenna_resepti(): formReseptit = ReseptitForm() if formReseptit.validate(): resepti = Resepti() try: # luo uuden keyn urlsafe-muodossa olleesta avaimesta resepti.key = ndb.Key(urlsafe=formReseptit.key.data) except: pass resepti.nimi = formReseptit.nimi.data resepti.kuvaus = formReseptit.kuvaus.data resepti.henkilomaara = formReseptit.henkilomaara.data resepti.ruokalaji = formReseptit.ruokalaji.data.key resepti.put() return redirect(url_for('reseptit', resepti_id=resepti.key.urlsafe())) return reseptit(None) - Miten hyvin toimii reseptilistaus muokkaamisen ja lisäämisen jälkeen? Näkyykö lisäys tai muokkaus heti listauksessa? Jos ei näy niin varmista, että haet muokattavan tai lisätyn reseptin datastoresta (key.get() ennen kuin haet listausta kaikista resepteistä. Aihetta on käsitelty tarkemmin seuraavissa dokumenteissa:
- Yritä tehdä samaan tapaan myös ruokalajit muokattaviksi
Jos sovellus kaatuu hämärään KeyPropertyField-virheeseen niin kyseessä voi olla tunnettu bugi, joka pitäisi olla jo korjattu valmiissa pohjassa olevaan kirjastoon, mutta jos itse päivität kirjastoja niin saatat saada saman bugin takaisin.
Kirjautuminen Googlen tunnuksella
Appengine-sovellukseen on helppo lisätä kirjautuminen Googletunnuksella: User authentication in Python
Lisää sovellukseesi seuraava sivu. Kokeile ladata sivu sekä lokaalilla palvelimelle, että pilvessä julkaistulla versiolla. Lokaali versio ei käytä oikeaa Googlen autentikointia vaan emuloi sitä.
# Kirjaudutaan sisään käyttäen googlen autentikointia
@app.route('/login')
def login():
user = users.get_current_user()
if user:
nickname = user.nickname()
logout_url = users.create_logout_url('/')
greeting = 'Welcome, {}! (<a href="{}">sign out</a>)'.format(
nickname, logout_url)
else:
login_url = users.create_login_url('/')
greeting = '<a href="{}">Sign in</a>'.format(login_url)
return '<html><body>{}</body></html>'.format(greeting)
Jos haluat tallentaa kirjautuneen käyttäjän uniikin tunnisteen esim. tietokantaan niin sen saat user_id()-metodilla.
tunniste = user.user_id()
Kirjautuneen käyttäjän sähköpostiosoitteen saat email()-metodilla. Älä kuitenkaan käytä sähköpostiosoitetta käyttäjän uniikkina tunnisteena, koska osoite voi muuttua.
Kts. google.appengine.api.users module.
Voit lisätä autentikoinnin app.yaml-tiedoston avulla joillekin halutuille sivuille tai kaikille sivulle. Lue app.yaml handlers login. Tee app.yaml-tiedostoon seuraava muutos:
- url: /.*
script: main.app
login: required
Kokeile joudutko nyt kirjautumaan ennen reseptisivulle pääsemistä. Kokeile selaimella, jossa et ole jo valmiiksi kirjautuneena googleen tai käytä private-tilassa olevaa selainikkunaa.
web2py
Jos halut kokeilla web2py-frameworkia niin lue vanha ohjaustehtävä

Käyttäjien kommentit