2.1 Architettura generale
In questa fase progetteremo l'architettura di un'applicazione per la gestione delle attività. Definiremo come interagiranno il frontend, il backend e il database, oltre a quali componenti saranno inclusi in ciascuno di essi.
L'app sarà composta da tre componenti principali:
- Frontend (ReactJS): la parte client che assicura l'interazione dell'utente con il sistema.
- Backend (Flask): la parte server che gestisce le richieste dal frontend e interagisce con il database.
- Database (PostgreSQL): il sistema di archiviazione dei dati per utenti e attività.
L'architettura sarà così strutturata:
+-------------+ +-------------+ +--------------+
| | | | | |
| Frontend +------->+ Backend +------->+ Database |
| (ReactJS) | | (Flask) | | (PostgreSQL) |
| | | | | |
+-------------+ +-------------+ +--------------+
Interazione tra i componenti
- Frontend: invia richieste HTTP al backend per eseguire operazioni CRUD (creazione, lettura, aggiornamento, eliminazione delle attività).
- Backend: gestisce le richieste HTTP dal frontend, esegue la logica di business e interagisce con il database.
- Database: memorizza e fornisce dati su richiesta dal backend.
2.2 Descrizione di ogni componente
1. Frontend (ReactJS):
- Componenti dell'interfaccia: componenti per la registrazione e l'autenticazione degli utenti, la creazione e la modifica delle attività, la visualizzazione dell'elenco delle attività.
- Interazione con API: utilizzo della libreria Axios per inviare richieste HTTP al backend.
2. Backend (Flask):
- REST API: implementazione degli endpoint per la gestione degli utenti e delle attività.
- Modelli di dati: definizione dei modelli di dati per utenti e attività utilizzando SQLAlchemy.
- Logica di business: gestione della logica dell'applicazione, inclusa la validazione dei dati e la gestione delle sessioni utenti.
3. Database (PostgreSQL):
- Tabelle: tabelle per memorizzare informazioni su utenti e attività.
- Relazioni tra tabelle: definizione delle relazioni tra le tabelle degli utenti e delle attività (ad esempio, un utente può avere molte attività).
4. Interazione di rete
Tutta la comunicazione tra i componenti avverrà tramite il protocollo HTTP. Il frontend interagirà con il backend tramite REST API, mentre il backend interagirà con il database tramite query SQL.
- Frontend: porta 3000 per sviluppo e test.
- Backend: porta 5000 per interazione col frontend.
- Database: porta 5432 per interazione col backend.
2.3 Descrizione dettagliata di ogni componente
1. Struttura base dei dati
Per memorizzare i dati degli utenti e dei task nel database PostgreSQL, creeremo due tabelle: users
e tasks
.
Tabella users
:
-
id (int, primary key)
: identificatore univoco dell'utente. username (varchar, unique)
: nome dell'utente.password (varchar)
: hash della password dell'utente.
Tabella tasks
:
-
id (int, primary key)
: identificatore univoco del task. title (varchar)
: titolo del task.description (text)
: descrizione del task.-
owner_id (int, foreign key)
: identificatore dell'utente al quale è assegnato il task. -
status (varchar)
: stato del task (ad esempio, completato/non completato).
2. Design dell'API
Il backend fornirà un'API RESTful per interagire con il frontend. Elenco approssimativo degli endpoint:
- Utenti:
- POST /users: creazione di un nuovo utente.
- GET /users: ottenimento della lista di tutti gli utenti.
- GET /users/:id: ottenimento delle informazioni su un utente specifico.
- PUT /users/:id: aggiornamento delle informazioni di un utente.
- DELETE /users/:id: eliminazione di un utente.
- Task:
- POST /tasks: creazione di un nuovo task.
- GET /tasks: ottenimento della lista di tutti i task.
- GET /tasks/:id: ottenimento delle informazioni su un task specifico.
- PUT /tasks/:id: aggiornamento delle informazioni di un task.
- DELETE /tasks/:id: eliminazione di un task.
2.4 Modelli di dati
Questo è come apparirà il codice in Python per lavorare con le tabelle del database:
Modello User:
from app import db
class User(db.Model):
id = db.Column(db.Integer, primary_key=True)
username = db.Column(db.String(80), unique=True, nullable=False)
password = db.Column(db.String(120), nullable=False)
tasks = db.relationship('Task', backref='owner', lazy=True)
def to_dict(self):
return {
"id": self.id,
"username": self.username
}
Modello Task:
from app import db
class Task(db.Model):
id = db.Column(db.Integer, primary_key=True)
title = db.Column(db.String(120), nullable=False)
description = db.Column(db.Text, nullable=True)
owner_id = db.Column(db.Integer, db.ForeignKey('user.id'), nullable=False)
status = db.Column(db.String(20), nullable=False, default="non completata")
def to_dict(self):
return {
"id": self.id,
"title": self.title,
"description": self.description,
"owner_id": self.owner_id,
"status": self.status
}
2.5 Rotte e controller
Esempio di implementazione dell'API lato server:
from app import app, db
from app.models import Task, User
from flask import request, jsonify
@app.route('/tasks', methods=['GET'])
def get_tasks():
tasks = Task.query.all()
return jsonify([task.to_dict() for task in tasks])
@app.route('/tasks', methods=['POST'])
def create_task():
data = request.get_json()
new_task = Task(
title=data['title'],
description=data.get('description'),
owner_id=data['owner_id'],
status=data.get('status', "non completata")
)
db.session.add(new_task)
db.session.commit()
return jsonify(new_task.to_dict()), 201
@app.route('/tasks/<int:id>', methods=['GET'])
def get_task(id):
task = Task.query.get_or_404(id)
return jsonify(task.to_dict())
@app.route('/tasks/<int:id>', methods=['PUT'])
def update_task(id):
data = request.get_json()
task = Task.query.get_or_404(id)
task.title = data['title']
task.description = data.get('description')
task.status = data.get('status', task.status)
task.owner_id = data['owner_id']
db.session.commit()
return jsonify(task.to_dict())
@app.route('/tasks/<int:id>', methods=['DELETE'])
def delete_task(id):
task = Task.query.get_or_404(id)
db.session.delete(task)
db.session.commit()
return '', 204
2.6 Esempio di richiesta al server dal frontend
Esempio di componente React per visualizzare una lista di task:
import React, { useEffect, useState } from 'react';
import axios from 'axios';
const TaskList = () => {
const [tasks, setTasks] = useState([]);
useEffect(() => {
axios.get('http://localhost:5000/tasks')
.then(response => {
setTasks(response.data);
})
.catch(error => {
console.error('C'è stato un errore mentre si recuperavano i task!', error);
});
}, []);
return (
<div>
<h1>Lista dei Task</h1>
<ul>
{tasks.map(task => (
<li key={task.id}>{task.title} - {task.status}</li>
))}
</ul>
</div>
);
};
export default TaskList;
GO TO FULL VERSION