Warning
This page is located in archive. Go to the latest version of this course pages. Go the latest version of this page.

3. Propojení aplikace s SQLite

Cíle cvičení

  • Vyzkoušet si práci s SQL databází (+ VS Code plugin )
  • Vyzkoušet šablony ve Flask Aplikaci

Ukládání dat do SQL databáze

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.

Knihovna pro práci s databází

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()

Hlavní program

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)

Rozšiřte kód tak, aby vložil do databáze větši množství náhodných hodnot, které budou simulovat fluktuaci hodnot senzorů. Pokuste se co nejlépe simulovat časovou řadu.

Další funkcionalita frameworku Flask

Přesměrování

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)

Šablona

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 %}

Kmbinace šablony a přesměrování

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>

Pro stylování HTML dokumetu je použit framework Bootstrap. Pokud vás zajímají podrobnosti, zkuste dokumentaci nebo tutorial.

Zadání 2. miniprojektu

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.

Při propojování s databází možná narazíte na problémy, které souvisí s tím, že modul 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).
courses/b0b37nsi/tutorials/03.txt · Last modified: 2023/03/10 07:57 by viteks