- Olet asentanut kurssilla tarvittavat työvälineet.
- Olet lukenut materiaalissa olevan lyhyen Python-oppaan ja kokeillut siinä olevia esimerkkejä.
-
Olet tutustunut web-sovellusten toimintaan ja toteuttanut tämän sivun ohjeistusta mukaillen ensimmäisen Flaskia käyttävän web-sovelluksesi.
HUOM! Tarkoituksena on harjoitella sovelluksen toteuttamisen alkeita, älä kopioi tätä palautettavaan repositorioon! - Olet luonut tämän sivun ohjeistusta noudattaen projektille sivun Herokuun. Tämän osan tarkoituksena on harjoitella työkalujen käyttöä, ei vielä aloittaa omaa projektia.
Johdanto
Tämän osan alussa tarkastellaan web-sovelluksia, jonka jälkeen harjoitellaan oman sovelluksen tekemistä. Tämä osa on puhtaasti työkalujen käytön harjoittelua ja opastusta. Oma projekti aloitetaan seuraavassa osassa.
Kurssimateriaalin esimerkkisovelluksia ei kannata seurata kirjaimellisesti omaa työtä tehdessä. Esimerkkisovellus ei kokonaisuutena kata kurssin vaatimuksia, vaan on tarkoitettu esimerkiksi jonka kautta materiaalia voi soveltaa omaan työhönsä.
Web-sovellusten perusteet
Web-sovellukset koostuvat selain- ja palvelinpuolesta. Käyttäjä käyttää selainohjelmistoa (esim. Chrome, kännykän selain, pädin selain, Kindlen selain, ...), joka tekee käyttäjän toimien perusteella pyyntöjä verkossa sijaitsevalle palvelimelle. Kun palvelin vastaanottaa pyynnön, se käsittelee pyynnön ja rakentaa vastauksen. Vastaus voi sisältää esimerkiksi web-sivun HTML-koodia tai jossain muussa muodossa olevaa tietoa.
Palvelin on kone aivan samalla tavalla kuin kännykkä, kannettava tietokone, pädi ym on kone. Kumpikin on kiinni verkossa, ja kummallakin on (ainakin välillisesti) koneen tunnistava IP-osoite. Kun käyttäjä tekee selainohjelmistolla pyynnön, pyyntö ohjautuu käyttäjän koneen verkkoyhteyden kautta palvelimen koneelle, jossa palvelinohjelmisto vastanottaa pyynnön ja käsittelee sen. Vaikka käyttäjä hakee tyypillisesti tekstimuotoisia osoitteita kuten https://www.helsinki.fi/fi, ei koneet näistä ymmärrä -- verkossa on erilliset palvelut tekstimuotoisten osoitteiden muuntamiseksi IP-osoitteiksi.
Selaimen tekemät pyynnöt noudattavat HTTP-protokollaa, joka on määritelty standardi pyyntöjen tekemiseen sekä niiden käsittelyyn. Resurssien -- eli tietyn osoitteen polkujen -- hakeminen tapahtuu HTTP-protokollan GET-tyyppisillä pyynnöillä, kun taas tiedon lähettäminen tapahtuu HTTP-protokollan POST-tyyppisillä pyynnöillä.
Selaimet tarjoavat tyypillisesti mahdollisuuden haettavien resurssien tarkasteluun. Esimerkiksi Google Chromessa selaintyövälineet saa auki painamalla näppäinyhdistelmää Ctrl+Shift+I
tai valitsemalla Chromen valikosta Developer Tools
. Selaimen työvälineet näyttävät seuraavalta.
Kun selaimeen kirjoittaa osoitteen -- alla olevassa esimerkissä https://www.helsinki.fi/fi -- tekee selain GET-tyyppisen pyynnön osoitteeseen eli hakee osoitteessa olevalta palvelimelta polun osoittamaa resurssia.
Kun palvelin palauttaa resurssin, tyypillisesti HTML-kielisen sivun, voi resurssissa olla linkkejä myös toisiin resursseihin. Selaimet on ohjelmointu siten, että esimerkiksi HTML-kielisen sivun resurssit haetaan automaattisesti. Yhtä sivua haettaessa saatetaan noutaakin todellisuudessa kymmeniä ellei satoja resursseja.
Yllä olevassa kuvassa osoitteessa https://www.helsinki.fi/fi olevan sivun lataus on vielä kesken. Sivua rakennettaessa on tehty 101 pyyntöä, ja tietoa on siirretty 1.6 megatavua.
Web-sovellusten kehitys on tapana jakaa selainohjelmistojen kehitykseen ja palvelinohjelmistojen kehitykseen, vaikkakin nykyään molemmat kattava fullstack-kehitys on yhä suositumpaa.
Selainohjelmistoja ja käyttöliittymää kehitettäessä painotetaan rakenteen, ulkoasun ja toiminnallisuuden erottamista toisistaan. Karkeasti voidaan sanoa, että selaimessa näkyvän sivun sisältö ja rakenne määritellään HTML-tiedostoilla, ulkoasu CSS-tiedostoilla ja toiminnallisuus JavaScript-tiedostoilla.
Palvelinpuolen toiminnallisuutta toteutettaessa keskitytään tyypillisesti selainohjelmiston tarvitsevan "APIn" suunnitteluun ja toteutukseen, sivujen muodostamiseen selainohjelmistoa varten, datan tallentamiseen ja käsittelyyn, sekä sellaisten laskentaoperaatioiden toteuttamiseen, joita selainohjelmistossa ei kannata tai voida tehdä.
Web-kehitys Flask-kirjastolla
Harjoitustyössä käytetään Pythonin Flask-kirjastoa web-sovelluksen toiminnallisuuden toteuttamiseen. Tutustutaan lyhyesti pienen Flask-sovelluksen toteuttamiseen.
Virtuaaliympäristön luominen
Luodaan ensin hakemisto sovellustamme varten ja luodaan sen sisälle Python-virtuaaliympäristö komennolla python3 -m venv venv
. Kutsutaan sovellusta nimellä demo
.
$ ls $ mkdir demo $ ls demo $ cd demo ~/demo$ python3 -m venv venv ~/demo$ ls venv $
Nyt luomallamme virtuaaliympäristöllä on kansio venv
. Tarkastellaan sen sisältöä.
~/demo$ ls venv bin include lib lib64 pyvenv.cfg share $
Kansiossa on kansiot bin
, include
, lib
ja share
. Kansio lib64
on todellisuudessa linkki kansioon lib
. Tiedosto pyvenv.cfg
sisältää virtuaaliympäristön asetukset.
Kun haluamme ottaa luodun virtuaalisen Python-ympäristön käyttöön, aktivoimme sen komennolla source venv/bin/activate
. Muuttuneesta komentokehoitteesta näkee että virtuaaliympäristö on aktiivinen.
~/demo$ source venv/bin/activate (venv) ~/demo$
Haluamme lisätä käyttöömme Flask-kirjaston. Tämä onnistuu kansiossa bin
olevalla pip
-kemonnolla.
(venv) ~/demo$ pip install Flask .... .... You are using pip version 8.1.1, however version 9.0.1 is available. You should consider upgrading via the 'pip install --upgrade pip' command. (venv) ~/demo$
Flask-kirjaston asentamisen yhteydessä saimme tiedon siitä, että käytössä oleva pip on vanhahko. Päivitetään se samalla.
(venv) ~/demo$ pip install --upgrade pip Collecting pip Using cached pip-9.0.1-py2.py3-none-any.whl Installing collected packages: pip Found existing installation: pip 8.1.1 Uninstalling pip-8.1.1: Successfully uninstalled pip-8.1.1 Successfully installed pip-9.0.1 (venv) ~/demo$ ls venv bin include lib lib64 pip-selfcheck.json pyvenv.cfg share (venv) ~/demo$
Käytössämme on nyt Flask, jonka lisäksi versionhallinnassa käytetyn pipin versiota on päivitetty.
Ensimmäinen Flask-sovellus
Flask-kirjaston etusivulla on seuraava lähdekoodi.
from flask import Flask
app = Flask(__name__)
@app.route("/")
def hello():
return "Hello World!"
Luodaan uusi tiedosto hello.py
edellä luotuun demo-kansioomme ja kopioidaan esimerkkilähdekoodi tiedoston hello.py
sisällöksi. Suoritetaan tämän jälkeen tiedoston hello.py
lähdekoodi.
(venv) ~/demo$ ls hello.py venv (venv) ~/demo$ python3 hello.py (venv) ~/demo$
Mitään ei tapahtunut. Huraa?
Flask-sovellusten käynnistäminen suoraan tiedostosta vaatii sovellukseen vielä ohjeistuksen komentoriviltä käynnistämiseen. Lisätään hello.py
-tiedostoon vielä muutama rivi, jotka ohjeistavat sovelluksen käynnistämiseen kun se suoritetaan komentoriviltä.
from flask import Flask
app = Flask(__name__)
@app.route("/")
def hello():
return "Hello World!"
if __name__ == "__main__":
app.run()
Kokeillaan sovelluksen käynnistämistä uudestaan.
(venv) ~/demo$ ls hello.py venv (venv) ~/demo$ python3 hello.py * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
Sovellus on nyt käynnissä. Kun menemme selaimella osoitteeseen http://127.0.0.1:5000/, näemme seuraavanlaisen sivun.
Sovellus on käynnissä! Huomaamme, että komentoriville on ilmestynyt muutamia lisärivejä. Rivit kuvaavat ovat palvelinohjelmiston logirivejä ja ne kertovat palvelimelle tulleista pyynnöistä.
Palvelimelle on tullut oikeastaan kaksi pyyntöä, vaikka haimme sivua vain kerran. Ensimmäinen pyyntö "GET / HTTP/1.1"
haki tietoa sovelluksen juuripolusta. Tämä on oikeastaan juuri se, mitä sovelluksemme metodi hello()
käsittelee. Toinen pyyntö "GET /favicon.ico HTTP/1.1"
haki sivulle ikonia -- palvelimelta ei tällaista löytynyt.
(venv) ~/demo$ ls hello.py venv (venv) ~/demo$ python3 hello.py * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit) 127.0.0.1 - - [29/Feb/2019 18:07:31] "GET / HTTP/1.1" 200 - 127.0.0.1 - - [29/Feb/2019 18:07:31] "GET /favicon.ico HTTP/1.1" 404 -
Palvelimen sammuttaminen onnistuu komentoriviltä painamalla CTRL+C
, eli näppäimiä ctrl
ja c
samanaikaisesti.
Debug-moodi
Lähdekooditiedoston hello.py
suorittaminen käynnistää palvelinohjelmiston.
(venv) ~/demo$ ls hello.py venv (venv) ~/demo$ python3 hello.py * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
Kun sovellus on käynnissä, muokkaukset tiedostoon hello.py
eivät näy kun tietoa haetaan selaimella uudestaan. Ohjelmistokehityksen nopeuden kannalta olisi kuitenkin hyödyllistä, jos muutoksia voisi tarkastella ilman että palvelinta käynnistellään jatkuvasti uudelleen.
Flaskille voi määritellä ns. debug-moodin, mikä muuntaa sovelluksen toimintaa muunmuassa siten, että sovelluksen uudelleenkäynnistäminen muutosten yhteydessä ei ole tarpeellista. Muokataan sovelluksen käynnistävää tiedostoa hello.py
siten, että komento app.run()
saa parametrinaan tiedon debug-tilasta. Tämä onnistuu seuraavasti.
from flask import Flask
app = Flask(__name__)
@app.route("/")
def hello():
return "Hello World!"
if __name__ == "__main__":
app.run(debug=True)
Kun sovelluksen sammuttaa ja käynnistää uudelleen, selaimella sivu näyttää yhä seuraavalta.
Terminaalissa tosin tulostus on hieman erilainen.
(venv) ~/demo$ ls hello.py venv (venv) ~/demo$ python3 hello.py * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit) * Restarting with stat * Debugger is active! * Debugger PIN: 231-450-049
Muokataan tiedostoa hello.py
siten, että sivun pitäisi näyttää teksti "Hei maailma!".
from flask import Flask
app = Flask(__name__)
@app.route("/")
def hello():
return "Hei maailma!"
if __name__ == "__main__":
app.run(debug=True)
Kun tallennamme tiedoston, huomaamme, että terminaalissa näkyvä tulostus päivittyy.
(venv) ~/demo$ ls hello.py venv (venv) ~/demo$ python3 hello.py * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit) * Restarting with stat * Debugger is active! * Debugger PIN: 231-450-049 * Detected change in '/polku/demo/hello.py', reloading * Restarting with stat * Debugger is active! * Debugger PIN: 231-450-049
Kun sivun lataa selaimessa uudestaan, sivulla näkyy nyt päivitetty teksti.
Debug-tilasta on muitakin hyötyjä. Pääset esimerkiksi tarkastelemaan selaimesta sovelluksen tilaa virhetilanteen yhteydessä. Tämän takia on myös hyvä varmistaa, ettei debug-tila ole koskaan päällä kun sovellus viedään tuotantoon -- debug-tila mahdollistaa myös mm. tietokannan salasanojen ym tarkastelun. Ei hyvä asia tuotannossa.
HTML-näkymien luominen
Tutustutaan seuraavaksi HTML-kielellä toteutettavien näkymien luomiseen. Mikäli HTML-kieli ei ole tuttu, käy läpi ainakin osoitteessa https://developer.mozilla.org/en-US/docs/Learn/HTML oleva opas.
Jatketaan aiemmin luodun sovelluksen parissa.
(venv) ~/demo$ ls hello.py venv
Luodaan HTML-näkymiä varten kansio nimeltä templates
ja mennään kansioon.
(venv) ~/demo$ mkdir templates (venv) ~/demo$ cd templates (venv) ~/demo/templates$ ls (venv) ~/demo/templates$
Kansio on toistaiseksi tyhjä. Luodaan kansioon kaksi tiedostoa layout.html
ja index.html
. Tiedostoon layout.html
tulee sivun yleinen rakenne, menuvalikko ym. Tiedostoon index.html
taas sivun etusivun sisältö.
Asetetaan sivun layout.html
sisällöksi seuraava.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Demo</title>
</head>
<body>
{% block body %}
<p>
Tähän tuodaan sisältö muualta.
</p>
{% endblock %}
</body>
</html>
Ja sivun index.html
sisällöksi seuraava.
{% extends "layout.html" %}
{% block body %}
<p>
Hei maailma!
</p>
{% endblock %}
Flask käyttää oletuksena Jinja-nimistä kirjastoa käyttäjälle näytettävien sivujen luomiseen. Jinja on hieman tietokantojen perusteista tutun Thymeleafin kaltainen kirjasto, jota käytetään HTML-sivujen luomiseen. Jinja mahdollistaa myös esimerkiksi toistolauseiden ym. käyttämisen osana HTML-näkymiä -- sivut luodaan palvelimella.
Nyt sovelluksemme kansion templates
sisällön pitäisi olla seuraavanlainen.
(venv) ~/demo/templates$ ls index.html layout.html (venv) ~/demo/templates$
Palataan tiedostohierarkiassa aiempaan kansioon.
(venv) ~/demo/templates$ ls index.html layout.html (venv) ~/demo/templates$ cd .. (venv) ~/demo$
Muokataan kansiossa olevaa hello.py
-tiedostoa. Emme näytäkään käyttäjälle suoraan merkkijonoa Hei maailma!
, vaan luomme käyttäjälle näkymän juuri luomiemme tiedostojen pohjalta.
Tarvitsemme sovelluksen käyttöön Flaskin render_template
-funktion. Funktio lisätään sekä tiedoston alussa olevaan import-lauseeseen, että pyynnön käsittelevään funktioon hello
, jossa kyseistä funktiota kutsutaan. Funktiolle render_template
annetaan kutsuttaessa parametrina HTML-tiedoston nimi, josta käyttäjälle palautettava sivu luodaan.
from flask import Flask, render_template
app = Flask(__name__)
@app.route("/")
def hello():
return render_template("index.html")
if __name__ == "__main__":
app.run(debug=True)
Käynnistetään sovellus.
(venv) ~/demo$ ls hello.py templates venv (venv) ~/demo$ python3 hello.py * Running on http://127.0.0.1:5000/ (Press CTRL+C to quit) * Restarting with stat * Debugger is active! * Debugger PIN: 231-450-049
Kun menemme osoitteeseen http://127.0.0.1:5000/, näemme seuraavanlaisen sivun.
Nopealla vilkaisulla mikään ei ole muuttunut. Kun tarkastelemme sivuun liittyvää lähdekoodia, huomaamme kuitenkin selkeitä muutoksia. Lähdekoodin tarkastelu onnistuu selaimessa klikkaamalla sivua oikealla hiirennäppäimellä ja valitsemalla View page source
(pikanäppäin Ctrl+S
).
Lähdekoodi paljastaa totuuden. Näyttää siltä, että sivun lähdekoodin runko on tullut templates
-kansion tiedostosta layout.html
ja sisältö tiedostosta index.html
.
Voimme siis määritellä sivustolle ulkoasun tiedostossa layout.html
. Sivukohtainen sisältö määritellään taas sivukohtaisessa tiedostossa -- esimerkiksi juurisivun sisältö löytyy tiedostosta index.html
.
Tiedon lisääminen näkymään
Funktiolle render_template
voidaan antaa parametreina muuttujia, joiden arvoja voidaan lisätä näkymiin. Tarkastellaan tässä muuttujien, listojen sekä olioita sisältävien listojen käsittelyä.
Näkymien luomiseen käytettävä Jinja-kirjasto etsii HTML-koodista kaksinkertaisilla aaltosuluilla rajattuja alueita sekä niiden sisällä olevaa koodia. Mikäli aaltosulkujen sisältä löytyy render_template
-funktiolle parametrina annetun muuttujan nimi, aaltosulkujen paikalle vaihdetaan muuttujan arvo.
Kontrollirakenteita kuten if
ja for
voi käyttää Jinjassa yksinkertaisten aaltosulkujen avulla siten, että avaavaa aaltosulkua seuraa prosenttimerkki, ja lopettavaa aaltosulkua edeltää prosenttimerkki. Esimerkiksi {% for arvo in lista %}{{ arvo }}{% endfor %}
kävisi jokaisen listalla olevan arvon läpi ja lisäisi sen sivulle.
Luodaan kansioon templates
tiedosto demo.html
, jonka avulla demonstroidaan useampaa tapaa muuttujien käyttöön. Asetetaan tiedoston sisällöksi seuraava.
{% extends "layout.html" %}
{% block body %}
<p>
Hei maailma!
</p>
<p>
<!-- etsitään muuttujaa nimeltä nimi ja asetetaan sen arvo tähän -->
{{ nimi }}
</p>
<ul>
<!-- Yritetään käydä lista-nimistä tietorakennetta läpi.
Jokaiselle tietorakenteen alkiolle luodaan li-elementti,
jonka sisälle asetetaan alkion arvo. -->
{% for arvo in lista %}
<li>{{ arvo }}</li>
{% endfor %}
</ul>
<table>
<tr>
<th>Nimi</th>
</tr>
<!-- Yritetään käydä tietorakenne nimeltä esineet läpi.
Jokaiselle esineelle luodaan oma rivi taulukkoon.
Jokaista taulukon solua luodessa oletetaan, että
esine on olio, ja että alkiolla on muuttuja name.
Muuttujan arvo asetetaan solun arvoksi. -->
{% for esine in esineet %}
<tr>
<td>{{ esine.name }}</td>
</tr>
{% endfor %}
</table>
{% endblock %}
Edellä oletetaan, että käytössä on kolme muuttujaa: nimi
, lista
, ja esineet
. Tämän lisäksi jokaisella esineellä tulee olla muuttuja name
.
Muokataan taas tiedostoa hello.py
. Luodaan tiedostoon esinettä kuvaava luokka Item
, jolla on oliomuuttuja name
. Luodaan tiedostoon myös muuttujat nimi
, lista
, ja esineet
ja asetetaan näihin esimerkkiä varten arvot.
Luodaan lopulta erillinen funktio, joka kuuntelee polkuun /demo
tehtäviä pyyntöjä. Kun pyyntö vastaanotetaan, käyttäjälle palautetaan funktion render_template
luoma sivua kuvaava merkkijono. Funktiolle render_template
annetaan parametrina aiemmin luotua demosivua kuvaava demo.html
sekä muuttujat nimi
, lista
, ja esineet
. Jokaiselle muuttujalle asetetaan myös nimi kutsun funktiokutsun yhteydessä.
from flask import Flask, render_template
app = Flask(__name__)
class Item:
def __init__(self, name):
self.name = name
nimi = "Essi Esimerkki"
lista = [1, 1, 2, 3, 5, 8, 11]
esineet = []
esineet.append(Item("Eka"))
esineet.append(Item("Toka"))
esineet.append(Item("Kolmas"))
esineet.append(Item("Neljäs"))
@app.route("/")
def hello():
return render_template("index.html")
@app.route("/demo")
def content():
return render_template("demo.html", nimi=nimi, lista=lista, esineet=esineet)
if __name__ == "__main__":
app.run(debug=True)
Mikäli sovellus ei ole käynnissä, käynnistetään se.
(venv) ~/demo$ python3 hello.py
* Running on http://127.0.0.1:5000/ (Press CTRL+C to quit)
* Restarting with stat
* Debugger is active!
* Debugger PIN: 231-450-049
Osoitteessa http://127.0.0.1:5000/ on tuttu sivu, joka näyttää viestin "Hei maailma!". Kun menemme osoitteeseen http://127.0.0.1:5000/demo, pyyntö ohjautuu hello.py
-tiedoston funktiolle nimeltä content
. Näemme lopulta seuraavanlaisen sivun.
Sovelluksen tallentaminen ja siirto pilveen
Seuraava osuus saattaa vaikuttaa tutulta edellisestä osasta erityisesti GitHubin osalta, ja osittain ne ovatkin samanlaiset. Osassa 0 luotiin jo repositorio omaa aihetta varten. Nyt meillä onkin jo websovellus, joka tarvitsee myös tallentaa repositorioon. Sen lisäksi haluamme, että sovellus toimii muuallakin kuin omalla koneella. Käytämme tässä palvelintarjoaja Herokua, jolta saamme käyttöömme ilmaista tallennustilaa sovellustamme varten.
Älä tallenna tätä työtä samaan repositorioon, jonka teit Osassa 0. Tätä ei tarvitse myöskään palauttaa Labtooliin. Tämän osan tarkoitus on olla harjoittelua, jotta seuraavassa osassa aloitettava oma työ sujuu mallikkaasti ja helposti.
Luo harjoitustyölle github-repositorio
Luo github-tunnuksesi alle uusi repositorio. Kuten edellisessä osassa, anna repositorion nimeksi haluamasi nimi.
Siirrä sovellus Githubiin
Lisää ensin projektin kansioon .gitignore
-tiedosto, johon lisätään versionhallinnasta poisjätettävät tiedostot. Emme halua, että virtuaaliympäristön sisältämät binäärit ovat versionhallinnassa, joten lisätään tiedostoon rivi "venv". Tämä tarkoittaa sitä, että kansiota venv
ei lisätä versionhallintaan.
(venv) ~/demo$ echo "venv" > .gitignore (venv) ~/demo$ cat .gitignore venv (venv) ~/demo$
Saat luomasi github-repositorion osoitteen selville Githubista. Tässä oletetaan, että osoite on https://github.com/avihavai/tsoha.git
.
Luodaan projektikansiolle git-versionhallinta komennolla git init
. Tämä luo mahdollisuuden projektin versiointiin ja luo kansioon .git
-nimisen kansion.
(venv) ~/demo$ git init (venv) ~/demo$
Lisätään githubissa oleva repositorio kansion paikallisen versionhallinnan etäpisteeksi. Projektin osoite löytyy githubista (se on tyypillisesti muotoa https://github.com/käyttäjätunnus/projektin-nimi.git
.
(venv) ~/demo$ git remote add origin github-projektin osoite (venv) ~/demo$
Lisätään tiedostot githubiin.
(venv) ~/demo$ git add . (venv) ~/demo$ git push -u origin master ..- (venv) ~/demo$
Nyt projektin lähdekoodit löytyvät githubista.
Siirrä sovellus Herokuun
Sovelluksen Herokuun siirtämistä varten sovellukselle tulee lisätä web-palvelin, jota Heroku osaa käyttää. Valitsemme käyttöön Gunicorn-palvelimen.
~/demo$ source venv/bin/activate (venv) ~/demo$ pip install gunicorn Collecting gunicorn Downloading gunicorn-19.7.1-py2.py3-none-any.whl (111kB) 100% |████████████████████████████████| 112kB 2.0MB/s Installing collected packages: gunicorn Successfully installed gunicorn-19.7.1 (venv) ~/demo$
Jäädytetään seuraavaksi projektin tämän hetkiset riippuvuudet, jotta Heroku tietää mitä riippuvuuksia tulee asentaa. Tämä onnistuu komennolla pip freeze
. Komennon tulostus ohjataan tiedostoon requirements.txt
. Alla tehdään sekä jäädytys että tulostetaan riippuvuudet sisältävän tiedoston requirements.txt
sisältö.
(venv) ~/demo$ pip freeze > requirements.txt (venv) ~/demo$ cat requirements.txt click==6.7 Flask==0.12.2 gunicorn==19.7.1 itsdangerous==0.24 Jinja2==2.10 MarkupSafe==1.0 pkg-resources==0.0.0 Werkzeug==0.14.1 (venv) ~/demo$
Tiedostossa oleva rivi pkg-resources==0.0.0
aiheuttaa ongelmia Herokussa, joten poistetaan se toistaiseksi. Tiedoston requirements.txt
sisältö on tämän jälkeen seuraava.
(venv) ~/demo$ cat requirements.txt click==6.7 Flask==0.12.2 gunicorn==19.7.1 itsdangerous==0.24 Jinja2==2.10 MarkupSafe==1.0 Werkzeug==0.14.1 (venv) ~/demo$
Voit nyt tarvittaessa asentaa projektisi riippuvuudet requirements.txt
-tiedoston avulla komennolla pip install -r requirements.txt
. Jos jossain vaiheessa esimerkiksi kloonaat repositoriosi koodin toiselle koneelle ja aloitat siten työskentelyn "puhtaasta" koodista, voit asentaa riippuvuudet luomaasi uuteen virtuaaliseen ympäristöön.
Lisätään seuraavaksi projektille Procfile
-niminen tiedosto. Tiedosto Procfile
on Herokun odottama tiedosto, joka sisältää Herokun tarvitsemat ohjeet sovelluksen käynnistämiseen. Lisätään ohjeiksi "web: gunicorn --preload --workers 1 hello:app", eli käynnistä hello
.py-tiedostosta app
-sovellus web
-prosessina gunicorn
-palvelinta käyttäen.
(venv) ~/demo$ echo "web: gunicorn --preload --workers 1 hello:app" > Procfile (venv) ~/demo$ cat Procfile web: gunicorn --preload --workers 1 hello:app (venv) ~/demo$
Luodaan tämän jälkeen sovellukselle paikka Herokuun. Tässä oletuksena on, että käytössä on Herokun käyttäjätunnus sekä Herokun työvälineet komentoriville (Heroku CLI). Paikan luominen tapahtuu komennolla heroku create
, jota seuraa toivottu sovelluksen nimi. Sovelluksen nimen tulee olla uniikki, eli alla oleva tsoha-python-demo
tulee vaihtaa omaa sovellusta kuvaavaksi. Mikäli nimen jättää pois, Heroku luo satunnaisen nimen sovellukselle, mikä riittää tällä kurssilla.
(venv) ~/demo$ heroku create tsoha-python-demo Creating ⬢ tsoha-python-demo... done https://tsoha-python-demo.herokuapp.com/ | https://git.heroku.com/tsoha-python-demo.git (venv) ~/demo$
Nyt sovellukselle on paikka Herokussa. Lisätään vielä paikalliseen versionhallintaan tieto Herokusta.
(venv) ~/demo$ git remote add heroku https://git.heroku.com/tsoha-python-demo.git (venv) ~/demo$
Ja lähetetään projekti Herokuun.
(venv) ~/demo$ git add . (venv) ~/demo$ git commit -m "Initial commit" (venv) ~/demo$ git push heroku master Counting objects: 13, done. Delta compression using up to 4 threads. Compressing objects: 100% (11/11), done. Writing objects: 100% (13/13), 1.79 KiB | 0 bytes/s, done. Total 13 (delta 2), reused 0 (delta 0) remote: Compressing source files... done. remote: Building source: remote: remote: -----> Python app detected remote: -----> Installing python-3.6.4 remote: -----> Installing pip remote: -----> Installing requirements with pip remote: Collecting click==6.7 (from -r /tmp/build_eb15306f4a3962d45ca30ea90e9b4cb6/requirements.txt (line 1)) remote: Downloading click-6.7-py2.py3-none-any.whl (71kB) remote: Collecting Flask==0.12.2 (from -r /tmp/build_eb15306f4a3962d45ca30ea90e9b4cb6/requirements.txt (line 2)) remote: Downloading Flask-0.12.2-py2.py3-none-any.whl (83kB) remote: Collecting gunicorn==19.7.1 (from -r /tmp/build_eb15306f4a3962d45ca30ea90e9b4cb6/requirements.txt (line 3)) remote: Downloading gunicorn-19.7.1-py2.py3-none-any.whl (111kB) remote: Collecting itsdangerous==0.24 (from -r /tmp/build_eb15306f4a3962d45ca30ea90e9b4cb6/requirements.txt (line 4)) remote: Downloading itsdangerous-0.24.tar.gz (46kB) remote: Collecting Jinja2==2.10 (from -r /tmp/build_eb15306f4a3962d45ca30ea90e9b4cb6/requirements.txt (line 5)) remote: Downloading Jinja2-2.10-py2.py3-none-any.whl (126kB) remote: Collecting MarkupSafe==1.0 (from -r /tmp/build_eb15306f4a3962d45ca30ea90e9b4cb6/requirements.txt (line 6)) remote: Downloading MarkupSafe-1.0.tar.gz remote: Collecting Werkzeug==0.14.1 (from -r /tmp/build_eb15306f4a3962d45ca30ea90e9b4cb6/requirements.txt (line 7)) remote: Downloading Werkzeug-0.14.1-py2.py3-none-any.whl (322kB) remote: Installing collected packages: click, itsdangerous, Werkzeug, MarkupSafe, Jinja2, Flask, gunicorn remote: Running setup.py install for itsdangerous: started remote: Running setup.py install for itsdangerous: finished with status 'done' remote: Running setup.py install for MarkupSafe: started remote: Running setup.py install for MarkupSafe: finished with status 'done' remote: Successfully installed Flask-0.12.2 Jinja2-2.10 MarkupSafe-1.0 Werkzeug-0.14.1 click-6.7 gunicorn-19.7.1 itsdangerous-0.24 remote: remote: -----> Discovering process types remote: Procfile declares types -> web remote: remote: -----> Compressing... remote: Done: 42.7M remote: -----> Launching... remote: Released v3 remote: https://tsoha-python-demo.herokuapp.com/ deployed to Heroku remote: remote: Verifying deploy... done. To https://git.heroku.com/tsoha-python-demo.git * [new branch] master -> master (venv) ~/demo$
Nyt sovellus on tarkasteltavana osoitteessa https://tsoha-python-demo.herokuapp.com/. Osoite on toki riippuvainen sovellukselle valitusta nimestä.
Sovellus on myös hyvä lähettää Githubiin, sillä siihen on tullut päivityksiä.
(venv) ~/demo$ git push origin master (venv) ~/demo$
Nyt kun työkalut on hallussa, on aika siirtyä oman harjoitustyön pariin!