Flask Avietėje

Posted: 2013-09-17 in Darbeliai
Žymos:, ,

Flask Microframework Web Development | Darau, blėKaip jau minėjau įraše apie Raspberry Pi ir Arduino bendravimą per I²C, visam tam reikalui apipavidalinti galvojau pasinaudoti labai įdomiu projektėliu FlaskFlask yra visiškai pitoninis reikalas, visas interneto serveris valdomas išimtinai Python pagalba. Tiesa, dalis esminių bibliotekų dėl našumo parašyta C kalba, kaip dažniausiai tokiais atvejais ir daroma. Taigi pagaliau sukaupiau lašelį laiko, kurį galėjau skirti šito termoso patyrinėjimui (Flask angliškai yra termosas, jei ką — o aš tikrinius pavadinimus mėgstu versti, kad niekas nesuprastų).

Ką gi, truputuką pasiskaičiau, kaip tas Flask naudojamas. Supratau, kad man jis patinka, nepaisant kelių smulkmenų. Laikas pradėti šito smagumo diegimą.

Diegimą pradėjau nuo virtualios Python aplinkos. Aš mėgstu tvarką ir visokių projektų izoliaciją. Kai šiaip darau kokį projektą, kurį reikės atiduoti, jam paprastai skiriu atskirą virtualią mašiną ir joje viską susireguliuoju, kas reikia. Na, Avietėje tokių nesąmonių nepridarysi, tai teks apsieiti. Bet yra toks gudrus Python išplėtimas, kuris padeda parsitempti visiškai atskirą Python rinkinį ir ten atskirai įsidiegti reikiamus paketus, kurių gali ten prireikti. Na, ir yra tikimybė, kad pagrindinė Python aplinka neužsikiš nereikalingu šlamštu bei po kokio nors atnaujinimo reikiama virtuali aplinka neapsivoš pilvu aukštyn. Taigi įdiegiu reikalingą pagelbą virtualių aplinkų kūrimui:

sudo apt-get install python-virtualenv

Dar susikuriu katalogėlį flask-site, kuriame dergsiuosi. O jame sukuriu ir naują aplinką:

pi@Uoga ~/flask-site $ virtualenv --system-site-packages env

Raktukas –system-site-packages turi šiokią tokią prasmę. Aš žadu šiame „projekte“ naudoti I2C magistralę, o tam reikia python-smbus paketo. O va virtualioje aplinkoje jo įdiegti neišeina, nes pip (tokia gimtoji Python įvairių paketų diegimo priemonė) šito paketo neturi. Kadangi nuo senesnių bandymų Pythonas jau turi python-smbus paketą, tai virtuali aplinka su nurodytu raktu paveldi visus jau įdiegtus paketus. O dabar jau galima virtualią aplinką aktyvuoti prasourcinus aplinką apibūdinantį konfigūracinį failiuką:

pi@Uoga ~/flask-site $ . flask/bin/activate

Komandinės eilutės pradžioje atsiranda virtualiosios aplinkos pavadinimas: čia kad vėpla programuotojas nepamirštų, kur randasi. Dabar jau su pip įnagėliu galima įdiegti patį flask:

(flask)pi@Uoga ~/flask-site $ pip install flask

Viskas, termosas suvažiavo sėkmingai. Galima dabar „Heliau vorld“ programulkę nusikopijuoti iš Flask tinklapio. Failas hello.py:

from flask import Flask
app = Flask(__name__)

@app.route("/")
def hello():
    return "Hello World!"

if __name__ == "__main__":
    app.run()

O dabar komandinėj eilutėj tą failą reikia įvykdyti ir stebėti, kas bus:

python hello.py
 * Running on http://127.0.0.1:5000/

Yra, kas yra. O yra tas, kad štai šitaip paleidžiamas interneto serveris su 5000 prievadu. Atsidarius jo adresą naršyklėje iš tiesų pamatau užrašą „Hello World!“. Nuostabu. Pirmas žingsnis žengtas, o dabar laikas gilintis kiek toliau. Išsiaiškinti, kokia yra termosinė šablonų aprašymo kalba ir kaip ją naudoti. Beje, užbėgdamas į priekį noriu pasidžiaugti, kad ta kalba nebloga, šablonai gali būti paveldintys — o tai labai patogu visiems kodytojams, nes mažiau visokio šlamšto rašinėti reikia.

Struktūra

Šiaip, žinoma, viso savo serverio į vieną failą nenoriu dėti. Apskritai, visas aplikacijas ruošiuosi dėti į atskirą paketą. Reikia išsiaiškinti, kaip tas daroma.

O daroma šitaip. Tarkime, šakniniame projekto kataloge paliekame kokį nors run.py failiuką, kuris iškvies pagrindinę aplikaciją. Tada galima sukurti paketą app ir ten įdėti kokią nors pradinę aplikaciją. Python paketas nuo paprasto katalogo skiriasi tuo, kad jame paprastai voliojasi __init__.py failiukas. Jei nežinojote, tai žinokite, nes dažnai pradedantys pitonistai šitą reikalą primiršta.

Taigi app kataloge sukuriame ir __init__.py failiuke galime sukurti pačios aplikacijos pagrindą. Failo turinys gali būti labai paprastas:

from flask import Flask

app = Flask(__name__)

Va ir viskas. Dabar run.py failiukyje galima importuoti šią aplikaciją ir paleisti:

from app import app

app.run(host='0.0.0.0', debug=True)

Aišku, tokia aplikacija nieko nedarys. Reikia bent jau tą patį „Heliau vorld“ įdėt kur nors. Pageidautina, į kokį nors atskirą failiuką, dėl bendros tvarkos. Pavadinkime jį main.py ir užpildykime kuo nors tokiu:

from app import app

@app.route('/')
@app.route('/index')
def index():
    return "Heliau vorld!"

Bet tai dar ne viskas. Reikia grįžti į aplikacijos failiuką __init__.py ir jo pabaigoje įrašyti import main. Na, kad aplikacija žinotų, jog į ją dar įeina ir puslapio vaizdą generuojantis modulis:

from flask import Flask

app = Flask(__name__)
import main

Dabar paleidus run.py programėlę ir naršyklėje įvedus localhost:5000 turėtų parodyti norimąjį „Heliau vorld!“. Kodas, manau gan aiškus. @app.route(‘…’) yra URI dekoratorius, juo nusakoma, kad tolimesnė funkcija atliks atitinkamo URI apdorojimą ir grąžins kokį nors turinį. Galima visiems URI kurti atskirus modulius ir juos importuoti aplikacijos __init__.py faile — aš tokį kelią ir pasirinkau. Smagiau viską išsiskaidyti.

Taigi turime dabar app paketą, kuris iš esmės ir bus visas interneto serveris. Bet, be abejo, reikia kažkaip gražiau apdoroti turinį ir jį formatuoti. Tam Flask turi integruotą Jinja2 šablonų varikliuką. Šablonai laikomi templates kataloge, kuris turi būti vienu lygiu žemiau, nei mūsų  kuriama aplikacija. Kadangi aplikacija šiuo atveju yra pakete app, tai to paketo viduje ir sukursime paprastą katalogą (ne Python paketą!) pavadinimu templates ir ten dėsime norimus HTML failus.

Yra labai didelis smagumas su Jinja2 šablonais — jie gali būti paveldimi. Taigi galime susikurti kažkokį tai pagrindinį šabloną, visą tinklapio „rėmą“, o paskui konkretūs puslapiai gali jį paveldėti ir aprašytus turinio blokus kažkuo užpildyti.

Pagrindinio karkaso pavyzdukas:

<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN">
<html lang="lt" xmlns="http://www.w3.org/1999/xhtml">
<head>
	<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    {% block head %}
    <link rel="stylesheet" href="{{ url_for('static', filename='style.css') }}" />
    <title>{% block title %}{% endblock %} - Mano ūkis</title>
    {% endblock %}
</head>
<body>
	<div id="main">
	    <nav id="main-nav">
	    	<ul id="top-menu">
	    		<li><a href="/">Pradžia</a></li>
	    		<li><a href="/statistics">Statistika</a></li>
    		</ul>
    	</nav>
	    <div id="content">{% block content %}{% endblock %}</div>
	    <div id="footer">
	        {% block footer %}
	        © 2013, Darau, blė.
	        {% endblock %}
	    </div>
    </div>
</body>

O kitas puslapėlis jį paveldi (atkreipkite dėmesį į eilutę „extends “main.html““:

{% extends "main.html" %}
{% block title %}Pradžia{% endblock %}
{% block head %}
    {{ super() }}

{% endblock %}
{% block content %}
    <h1>Pradžia</h1>
    <p class="important">
      Kas vyksta namuose:
      <ul>
      {% for s in sl -%}
      	<li>{{ s.friendly_name }}: {{ format_temp(s.value) }} {{ s.unit }}</li>
      {%- endfor %}
      </ul>
    </p>
{% endblock %}

Tai visai paprasta, gražu ir efektyvu.

Kad būtų aiškiau: skliausteliais ir procentukais išskiriami kažkokie vykdomieji blokai: turinio blokai, ciklai, sąlygos ir pan. Prieš procentuką be jokio tarpo padėjus minusiuką šablonų variklis nuvalo tuščias naujas eilutes, pagražina sugeneruotą HTML. Dvigubais skliausteliais apskliaudžiamos turinį sukuriančios išraiškos: išvedami kintamieji, pagalbinės funkcijos ir panašūs dalykai. „super()“ raktažodis veikia kaip ir visuose objektiniuose reikaluose: iškviečia paveldėto šablono atitinkamo bloko implementaciją.

Šablonai iškviečiami paprastai. Vietoj kokio nors return “Heliau Vorld“! reikia iškviesti šablonų paišymo funkciją:

from app import app
from flask import render_template
from sensors.persistence.psqlite import PSQLite

@app.route('/')
@app.route('/index')
def index():
    ps = PSQLite()
    sl = ps.loadSensorConfig()
    for s in sl:
        ps.getLastData(s)
    return render_template('index.html', sl=sl)

Čia per daug nesigilinkite, ką prikraigaliojau. Esmė tokia, kad iš duomenų bazės paimamas sąrašas kokių tai duomenų, o jie priskiriami kintamajam kartu su šablonu. Kintamasis „sl“ tampa prieinamas ir šablone — o šablono kode „for“ cikle jis ir yra imamas (jei atkreipėte dėmesį).

Su šablonais maždaug aišku. Kitas žingsniukas: visokie statiniai failai — paveiksliukai, stiliai ir kiti javascriptai. Jie slepiami atskirame kataloge, pavadinimu static. Mano atveju — paketo app viduje. Jei pažiūrėsite į pagrindinio karkaso šablono kodą, pamatysite, kad ten specialiu būdu iš „static“ katalogo imamas stilių failiukas „style.css“. Tas specialus būdas reikalingas tam, kad aplikacija būtų lengvai pernešama — galbūt ji bus įkišta į gilesnį paketą ar paketo pavadinimas bus pakeistas, ar dar kažkas. Tai kad nebūtų „kietai“ įrašytų kelių, naudojame štai tą specialią Flask funkciją url_for(…).

JSON

Man, be abejo, reikės ir AJAX linksmybių. Reikės šiam ir tam naudotis jQuery (kurio beveik nemoku, bet žinau, kas tas yr. Ir apskritai aš visai ne web-programuotojas). Na, o jQuery pasiiminėti duomenukus iš serverio geriausia JSON protokolu. Laimė, Flask šitas dalykas įgyvendintas puikiai: bet kokią Python struktūrą galima sukišti į kokį nors kintamąjį ir perleisti per specialią funkciją jsonify:

from app import app
from flask import render_template, jsonify

@app.route('/statistics:ajax:data')
def statistics_ajax_data():
    l = []
    l.append({'Bla': 1, 'Bu': 2}, {'Bla': 5, 'Bu': 4}]
    return jsonify(list=l)

Va ir viskas. Visokius sąrašus, žodynus, objektus ir panašiai galime tiesiog sugrūsti į jsonify ir priskirti norimiems kintamiesiems. Paskui su jQuery juos iš ten lengvai išsikapstyti.

favicon.ico

Priešpaskutinis reikalas šiame tyrime — maža pramogėlė. Kaipgi grąžinti tinklapį apipavidalinantį mažą paveiksliuką, vadinamą favicon? Tai paprasta. Reikia nusipaišyti kokį nors paveiksliuką 32×32 ar 16×16 pikselių dydžio ir išsaugoti, kaip „ico“ failą. Pasidėti jį galima bet kur, bet reikia, kad naršykė jį pasiimtų tarsi tiesiai iš šakninio tinklo serverio katalogo. Na, paprasčiausias būdas tai išsitraukti tą failiuką su Python ir prapūsti atgalios į naršyklę su nustatytu MIME tipu:

import os
from app import app
from flask import send_from_directory

@app.route('/favicon.ico')
def favicon():
    return send_from_directory(os.path.join(app.root_path, 'static'),
                               'favicon.ico', mimetype='image/vnd.microsoft.icon')

Aš susikurtą faviconą pakavojau static kataloge, tad iš ten jį ir pasigriebiau. Tiek reikalų.

SSL

Ką gi, o dabar paskutinė, rimtoji dalis. Nemėgstu atviru tekstu naršyti puslapių ir labai mėgstu SSL šifravimą. O čia aš ruošiuosi naršyti savo asmeninį serveriuką iš namų, tai minimalus saugumas tikrai nepamaišys. Todėl būtina išsiaiškinti, kaip Flask priversti dirbti su SSL.

Pirmiausia reikės pyopenssl bibliotekos. Ji yra kompiliuojama vietoje, todėl dar reikia ir python-dev. Pastarąjį aš įdiegiau pagrindinėje Python distribucijoje:

sudo apt-get install python-dev

Na, o pyopenssl susikompiliavau ir susitvarkiau virtualioje aplinkoje. Beje, visus reikalus aš čia pradžiai dariau kompiuteryje su Ubuntu, o paskui kartojau Raspberry Pi.

pip install pyopenssl

Avietė pareiškė, kad tiek python-dev, tiek  pyopenssl jau turi. Tai ir gerai, maža bėda. Toliau. Susiradau tinklapėlį, kuriame aprašyta, kaip Flask priversti dirbti su SSL:

http://kracekumar.com/post/54437887454/ssl-for-flask-local-development

Viskas gerai ir Ubuntu kompiuteryje suveikė. Liuks. O Avietėje — ne. Tad ta proga apsinaujinau pyopenssl:

pip install --upgrade pyopenssl

Padėjo iš dalies — pabiro kita klaida. Teko dar pasikapstyti ir susirasti informacijos:

http://librelist.com/browser//flask/2013/4/27/re-flask-is-there-a-way-of-serving-https-in-flask-solution/

Teko apsinaujinti ir flask virtualioje aplinkoje:

pip install --upgrade flask

Na ir štai, run.py kodas su veikiančia SSL aplinka:

# -*- coding: utf-8 -*-

import os
from app import app

from OpenSSL import SSL
context = SSL.Context(SSL.SSLv23_METHOD)
context.use_privatekey_file(os.path.join(app.root_path, 'certificates', 'server.key'))
context.use_certificate_file(os.path.join(app.root_path, 'certificates', 'server.crt'))

app.run(host='0.0.0.0', debug=True, ssl_context=context)

Kaip ir viskas liuks. Tikslas pasiektas, Flask pradėjau įsisavinti. Turiu visiškai iki panagių grynutėlį Pitoninį serveriuką. Toliau imsiuos daviklių statistikos atvaizdavimo grafikais. Jau supratau, kad amCharts — puikiausias sprendimas ir dėkui už tai vienam šio tinklaraščio komentatoriui prie įrašo apie daviklių informacijos kaupimą.

Parašykite komentarą

Įveskite savo duomenis žemiau arba prisijunkite per socialinį tinklą:

WordPress.com Logo

Jūs komentuojate naudodamiesi savo WordPress.com paskyra. Atsijungti /  Pakeisti )

Google photo

Jūs komentuojate naudodamiesi savo Google paskyra. Atsijungti /  Pakeisti )

Twitter picture

Jūs komentuojate naudodamiesi savo Twitter paskyra. Atsijungti /  Pakeisti )

Facebook photo

Jūs komentuojate naudodamiesi savo Facebook paskyra. Atsijungti /  Pakeisti )

Connecting to %s

Brukalų kiekiui sumažinti šis tinklalapis naudoja Akismet. Sužinokite, kaip apdorojami Jūsų komentarų duomenys.