Reallizzare una webapp con Python e bottle | Expense Tracker
- Andrea Pollini
- Basi Dati , Programmazione
- 20 Oct, 2023
In questo esercizio vedremo come realizzare una webapp con Python e bottle. La webapp che andremo a realizzare sarà un semplice gestionale per le spese. L’utente potrà inserire le spese e visualizzare un report con le spese inserite.
Requisiti
Per realizzare la webapp utilizzeremo il linguaggio di programmazione Python e il framework bottle. Inoltre utilizzeremo il database SQLite per memorizzare i dati.
Analisi
L’applicazione che andremo a realizzare sarà un semplice gestionale per le spese. L’utente potrà inserire le spese e visualizzare un report con le spese inserite. Ogni spesa avrà una descrizione, un importo e una data. L’utente potrà inserire, modificare e cancellare le spese. Ogni spesa potrà appartenere ad una categoria (stringa) e a più tag (stringa). L’utente potrà visualizzare le spese per categoria e per tag.
Progettazione
Diagramma ER e Diagramma Concettuale
SQL e creazione del database
Ecco il codice SQL per creare il database e popolarlo con i dati di esempio.
--- 1. Create expenses table
CREATE TABLE expenses (
id INTEGER PRIMARY KEY AUTOINCREMENT,
description TEXT NOT NULL,
amount REAL NOT NULL,
date TEXT NOT NULL,
category TEXT NOT NULL
);
--- 2. Create categories table
CREATE TABLE categories (
name TEXT PRIMARY KEY
);
--- 3. create tags table
CREATE TABLE tags (
name TEXT PRIMARY KEY
);
--- 4. create expenses_tags table
CREATE TABLE expenses_tags (
expense_id INTEGER NOT NULL,
tag_name TEXT NOT NULL,
PRIMARY KEY (expense_id, tag_name),
FOREIGN KEY (expense_id) REFERENCES expenses(id),
FOREIGN KEY (tag_name) REFERENCES tags(name)
);
-- Path: db_populate.sql
--- 1. Populate categories table
INSERT INTO categories (name) VALUES ('Food');
INSERT INTO categories (name) VALUES ('Transportation');
INSERT INTO categories (name) VALUES ('Entertainment');
INSERT INTO categories (name) VALUES ('Clothing');
INSERT INTO categories (name) VALUES ('Rent');
INSERT INTO categories (name) VALUES ('Health');
INSERT INTO categories (name) VALUES ('Other');
--- 2. Populate tags table
INSERT INTO tags (name) VALUES ('lunch');
INSERT INTO tags (name) VALUES ('dinner');
INSERT INTO tags (name) VALUES ('snacks');
INSERT INTO tags (name) VALUES ('coffee');
--- 3. Populate expenses table
INSERT INTO expenses (description, amount, date, category) VALUES ('Lunch at McDonalds', 5.99, '2019-01-01', 'Food');
INSERT INTO expenses (description, amount, date, category) VALUES ('Dinner at Burger King', 7.99, '2019-01-01', 'Food');
INSERT INTO expenses (description, amount, date, category) VALUES ('Snacks at 7-11', 2.99, '2019-01-01', 'Food');
INSERT INTO expenses (description, amount, date, category) VALUES ('Coffee at Starbucks', 3.99, '2019-01-01', 'Food');
INSERT INTO expenses (description, amount, date, category) VALUES ('Lunch at McDonalds', 5.99, '2019-01-02', 'Food');
INSERT INTO expenses (description, amount, date, category) VALUES ('Dinner at Burger King', 7.99, '2019-01-02', 'Food');
INSERT INTO expenses (description, amount, date, category) VALUES ('Snacks at 7-11', 2.99, '2019-01-02', 'Food');
INSERT INTO expenses (description, amount, date, category) VALUES ('Coffee at Starbucks', 3.99, '2019-01-02', 'Food');
--- 4. Populate expenses_tags table
INSERT INTO expenses_tags (expense_id, tag_name) VALUES (1, 'lunch');
INSERT INTO expenses_tags (expense_id, tag_name) VALUES (2, 'dinner');
INSERT INTO expenses_tags (expense_id, tag_name) VALUES (3, 'snacks');
INSERT INTO expenses_tags (expense_id, tag_name) VALUES (4, 'coffee');
INSERT INTO expenses_tags (expense_id, tag_name) VALUES (5, 'lunch');
Per installare bottlepy è sufficiente scaricare il file bottle.py e salvarlo nella stessa cartella del file python che andremo a creare.
Per creare il database SQLite è sufficiente salvare il codice SQL in un file con estensione .sql
e poi eseguirlo con il comando sqlite3 expensetracker.db < db_populate.sql
. In alternativa, salvato il file db_populate.sql
è possibile richiamarlo dal seguente file python:
import os,os.path
import sqlite3
DB_NAME = "expensetracker.db"
# load the DDL data from the file db_init.sql
db_ddl_data= ""
with open("db_init.sql", "r") as f:
db_ddl_data = f.read()
if os.path.exists(DB_NAME):
os.remove(DB_NAME)
con = sqlite3.connect(DB_NAME)
with con:
cur = con.cursor()
cur.executescript(db_ddl_data)
Ecco il codice dell’applicazione.
Codice Python
from bottle import route, run, template,view, static_file,request,post, redirect,get
import sqlite3
DB_NAME = "expensetracker.db"
con = sqlite3.connect(DB_NAME)
def expense_tuple_to_dict(expense_tuple):
return {"id":expense_tuple[0],"description":expense_tuple[1],"amount":expense_tuple[2],"date":expense_tuple[3],"category":expense_tuple[4]}
@get('/expenses')
@view('hello_template')
def hello():
cursor = con.execute("SELECT * FROM expenses")
expenses = []
for row in cursor:
print(expense_tuple_to_dict(row))
expenses.append(expense_tuple_to_dict(row))
return {"name":"Ciao a tutti",'expenses':expenses}
@post("/expenses")
def create_expense():
if request.forms.get('_method') == 'delete':
return delete_expense(request.forms.get('id'))
data = {
"date": request.forms.get('date'),
"description" : request.forms.get('description'),
"amount": request.forms.get('amount'),
"category": request.forms.get('category')
}
print(request.forms)
cursor = con.execute("INSERT INTO expenses (date,description,amount,category) VALUES (?,?,?,?)",
(data["date"],data["description"],data["amount"],data["category"]))
con.commit()
return redirect("/expenses")
def delete_expense(id):
cursor = con.execute("DELETE FROM expenses WHERE id = ?",(id,))
con.commit()
return redirect("/expenses")
@route('/static/<filename:path>')
def send_static(filename):
return static_file(filename, root='static/')
run(host='localhost', port=8080, debug=True)
Descrizione del codice
-
DB_NAME = "expensetracker.db"
: Questa riga definisce una variabile DB_NAME che contiene il nome del file del database SQLite in cui saranno memorizzate le informazioni sulle spese. Il file del database sarà chiamato “expensetracker.db”. -
con = sqlite3.connect(DB_NAME)
: Qui viene stabilita una connessione al database SQLite specificato nella variabile DB_NAME utilizzando il modulo sqlite3. La connessione è memorizzata nella variabile con e verrà utilizzata per eseguire query sul database. -
def expense_tuple_to_dict(expense_tuple)
: Questa è una funzione definita dall’utente chiamata expense_tuple_to_dict. Prende in input una tupla expense_tuple e restituisce un dizionario che rappresenta una singola spesa. I campi del dizionario includono “id”, “description”, “amount”, “date” e “category”, che corrispondono ai valori nella tupla. -
@route('/hello')
: Questo è un decorator che associa la funzione hello() alla route ‘/hello’. In altre parole, quando l’applicazione web riceve una richiesta HTTP per la route ‘/hello’, verrà eseguita la funzione hello(). -
@view('hello_template')
: Questo è un altro decorator che specifica il nome del modello da utilizzare per generare la risposta HTML. In questo caso, il modello chiamato ‘hello_template’ verrà utilizzato per generare la pagina HTML. -
def hello()
: Questa è la funzione principale che verrà eseguita quando si accede alla route ‘/hello’. All’interno di questa funzione:a. Si esegue una query SQL sul database per selezionare tutte le righe dalla tabella “expenses”.
b. Viene inizializzata una lista vuota chiamata expenses.
c. Per ogni riga risultante dalla query, viene chiamata la funzione expense_tuple_to_dict() per convertire la tupla in un dizionario rappresentante una spesa. Il dizionario risultante viene quindi aggiunto alla lista expenses.
d. Infine, viene restituito un dizionario che contiene una chiave “name” con il valore “Ciao a tutti” e una chiave “expenses” con il valore della lista expenses.
-
run(host='localhost', port=8080, debug=True)
: Questo comando avvia il server web locale sulla porta 8080 e abilita la modalità di debug. Ciò significa che l’applicazione sarà accessibile all’indirizzo"http://localhost:8080"
e verrà mostrato l’output di debug in caso di errori o eccezioni.
Templates
hello_template.tpl
% rebase('base.tpl',title='test')
<form action="/expenses" method="post">
Description: <input name="description" type="text" />
Amount: <input name="amount" type="number" min=0 step=0.01 />
Date: <input name="date" type="date" />
<input value="Create" type="submit" />
<select name="category">
<option value="food">Food</option>
<option value="transport">Transport</option>
<option value="housing">Housing</option>
<option value="utilities">Utilities</option>
<option value="clothing">Clothing</option>
<option value="health">Health</option>
<option value="insurance">Insurance</option>
<option value="household">Household</option>
<option value="personal">Personal</option>
<option value="education">Education</option>
<option value="entertainment">Entertainment</option>
<option value="gifts">Gifts</option>
<option value="other">Other</option>
</select>
</form>
<table class="table-auto">
<thead>
<tr>
<th>Date</th>
<th>Description</th>
<th>Amount</th>
<th>Category</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
%for expense in expenses:
<tr>
<td>{{expense['date']}}</td>
<td>{{expense['description']}}</td>
<td>{{expense['amount']}}</td>
<td>{{expense['category']}}</td>
<td>
<button class="bg-blue-500 hover:bg-blue-700 text-white font-bold py-2 px-4 rounded">
Edit
</button>
<form action="/expenses" method="post">
<input type="hidden" name="id" value="{{expense['id']}}" />
<input type="hidden" name="_method" value="delete" />
<button class="bg-red-500 hover:bg-red-700 text-white font-bold py-2 px-4 rounded">
Delete
</button>
</form>
</td>
</tr>
%end
</tbody>
</table>
%if name == 'World':
<h1 class="text-3xl font-bold underline">Hello {{name}}!</h1>
<p>This is a test.</p>
%else:
<h1 class="text-3xl font-bold underline">Hello {{name}}!</h1>
<p>How are you?</p>
%end
%for expense in expenses:
<p>You spent {{expense}} {{expense['category']}}</p>
%end
base.tpl
<!doctype html>
<html>
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<link href="/static/main.css" rel="stylesheet">
</head>
<body>
<h1 class="text-3xl font-bold underline">
{{title}}
</h1>
<div class="container mx-auto">
{{!base}}
</div>
</body>
</html>
Categories
- Architettura Dei Calcolatori (1)
- Basi Dati (22)
- Didattica (12)
- Programmazione (15)
- Sistemi Distribuiti (9)
- C++ (9)
- Formati Dati (2)
- Game Development (1)
- Gestione Progetto (2)
- Informatica (1)
- Programmazione Web (2)
- Javascript (1)
- Tpsit (4)
- Programmazione Ad Oggetti (3)
- Podcast (2)
- Python (4)
- Informazione E Comunicazione (2)
- Strumenti (1)
Tags
- Architettura
- Calcolatori
- Universita
- Scuola
- Esercizi Diagramma Er
- Esercizi Diagramma Concettuale
- Esercizi Entita Relazioni
- Esercizi Database
- Dati E Informazioni
- Aspetto Intensionale
- Aspetto Estensionale
- Informatica
- Teoria Basi Dati
- Differenza Tra Dati E Informazioni
- Diagramma Er
- Temi Svolti Maturita
- Esercizi Sql
- Esercizi Basi Dati
- Webapp Python
- Bottle Python
- Sqlite
- Python
- Python Sqlite
- Python Bottle
- Python Webapp
- Python Webapp Sqlite
- Python Webapp Bottle
- Maturita Informatica
- Esercizi Informatica
- Esercizi Simulazione Esame Informatica
- Docker
- Container
- Esercizi
- Esercizi C++
- Esercizi File
- Esercizi File C++
- Esercizi Funzioni
- Esercizi Array
- Esercizi Matrici
- Esercizi Stringhe
- Esercizi Struct
- Esercizi Struct C++
- Sistemi Distribuiti
- Formati Dati
- Json
- Xml
- Godot
- Game Dev
- Game Engine Open Source
- Tutorial
- Gpo
- Esercizi Gpo
- Esercizi Gestione Progetti
- Esercizi Gestione Progetti Organizzazione Aziendale
- Esercizi Gpo Dimensionamento Server
- Esecizi Gpo Cloud
- Esercizi Piano Di Progetto
- Teoria
- Teoria Algoritmi
- Algoritmi
- Corso
- C++
- Funzioni
- C
- Javascript
- Corsi Programmazione Web
- Java
- Design Pattern
- Unit Testing
- Wannabe Programmer Podcast
- Gestione File
- Corso Python
- Tutorial Python
- Fork
- Gestione Processi
- Python Multiprocessing
- Rust
- Linguaggio Di Programmazione
- Simulazioni
- Video
- Raylib
- Physarum
- Architetture Di Comunicazione
- Client Server
- Io T
- Tpsit
- Peer To Peer
- Didattica
- Corsi
- Informazione
- Shannon
- Entropia
- Scrittura
- Markdown
- Formattazione Testo
- Guida Rapida
- Cheatsheet
- Tecnologie
- Logica Binaria
- Algebra Di Boole
- Rappresentazione Dati