Pro uložení dat můžeme využít SQLite databázi. Její výhodou je, že pracuje s lokálním binárním souborem a má minimální nároky na paměť a výkon počítače - veškerá potřebná funkcionalita je totiž vytvářena knihovnou, která pracuje s binárním souborem.
Pro jednoduchost můžete využít modul nsidb
, který umožňuje vytvořit databázi a tabulku pro ukládání dvou hodnot typu float
reprezentující teplotu a vlhkost přečtenou z hypotetického senzoru. Hodnoty jsou funkci pro zápis předávány jako datový typ tuple
. Následující kód uložte do pracovního adresáře jako nsidb.py
.
import sqlite3 from sqlite3 import Error def db_conn(file): """ Create a database connection : param file: name of database file : return: """ conn = None try: conn = sqlite3.connect(file) except Error as e: print("Error", e) return conn def db_exec(conn, sql, data = None): """ execute a SQL command :param conn: Connection object :param sql: SQL statement """ try: c = conn.cursor() if data is not None: c.execute(sql, data) else: c.execute(sql) except Error as e: print(e) return c def db_create(conn): sql = """CREATE TABLE IF NOT EXISTS hodnoty (id integer PRIMARY KEY, timestamp integer NOT NULL, temperature integer NOT NULL, humidity integer NOT NULL);""" cur = db_exec(conn, sql) def db_insert(conn, data): """ Create a new record :param conn: Connection object :param data: tuple of data :return: ID of last inserted row """ sql = """ insert into hodnoty (timestamp, temperature, humidity) values (datetime(), ?, ?); """ cur = db_exec(conn, sql, data) conn.commit() return cur.lastrowid def db_fetch(conn): """ Fetch data from table :param conn: the Connection object :return: """ cur = db_exec(conn, "SELECT * FROM hodnoty") return cur.fetchall()
V hlavním programu je importován modul nsidb
a volány přislušné funkce z modulu. Program pro uložení dat může vypadat třeba takto:
from nsidb import * conn = db_conn("iot.db") db_create(conn) db_insert (conn, (3.14, 25.5)) rows = db_fetch (conn) for row in rows: print(row)
from flask import Flask, redirect, url_for app = Flask(__name__) @app.route('/') def main(): return redirect(url_for('hello', username='Peter')) # Also pass an optional URL variable @app.route('/hello/<username>') def hello(username): return 'Hello, {}'.format(username) if __name__ == '__main__': app.run(debug=True)
Pro správu šablon se v prostředí Flask implicitně používá engine jinja. Pro využití možností engine je třeba importovat z Flask submodul render_template
a dodržet strukturu, kterou engine předpokládá:
project-directory | +-- templates (for Jinja2 HTML templates) +-- static | +-- css +-- js +-- img
Jednoduchá šablona může vypadat třeba takto:
<!DOCTYPE html> <html lang="en"> <head> <meta charset="utf-8"> <title>Response Page</title> </head> <body> <h1>Hello, {{ username }}</h1> </body> </html>
Na místo username
se do šablony dosadí hodnota, která byla předána při volání funkce render_template
@app.route('/name/<name>') def process(name): return render_template('name.html', username=name)
Kromě dosazování hodnot a případně vyhodnocování výrazů je možné pracovat i s běžnými algoritmickými konstrukty.
Podmínka:
{% if ...... %} ...... {% elif ...... %} ...... {% else %} ...... {% endif %}
Cyklus:
{% for item in list %} ...... {% endfor %}
Stávající aplikaci rozšiřte o zpracování URL /login
from flask import Flask, render_template, redirect, url_for, request # ... @app.route('/login', methods=['GET', 'POST']) def login(): error = None if request.method == 'POST': if request.form['username'] != 'admin' or request.form['password'] != 'admin': error = 'Invalid Credentials. Please try again.' else: return redirect(url_for('main')) return render_template('login.html', error=error)<<
V pracovním adresáři vytvořte podadresář templates
a do něj umístěte následující soubor login.html
. Na stejné úrovni vytvořte ještě další adresář static
a do něj rozbalte obsah bootstrap.min.zip.
<html> <head> <title>NSI application - login page</title> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <link href="/static/bootstrap.min.css" rel="stylesheet" media="screen"> </head> <body> <div class="container"> <h1>Please login</h1> <br> <form action="" method="post"> <input type="text" placeholder="Username" name="username" value="{{ request.form.username }}"> <input type="password" placeholder="Password" name="password" value="{{ request.form.password }}"> <input class="btn btn-primary" type="submit" value="Login"> </form> {% if error %} <div class="alert alert-warning" role="alert"> <strong>Error:</strong> {{ error }} </div> {% endif %} </div> </body> </html>
Rozšiřte aplikaci vytvořenou v rámci 1. miniprojektu tak, aby byla propojena s SQL databází. Hlavním úkolem je zobrazení nasbírané časové řady, která je uložena v databázi. Pokud máte chuť a čas, rozšiřte databázi o tabulku s uživateli a zobrazujte data jen po autorizaci.
sqlite3
nepodporuje sdílení jedné instance více vlákny, které aplikce ve Flasku
vytváří pro odbavení dotazů na jednotlivá URL. Řešením (suboptimálním) je vytvářet lokální instance v jednotlivých funkcích. S ohledem na (demonstrační) účel aplikace to nijak nevadí. Pokud bychom od aplikace očekávali větší výkon, je lépe využít databázi, která sdílení instance umožňuje (nebo zavést v aplikaci frontu zpráv, jako např. zde).