Prossimo passo: progettare il database. Prima di creare le tabelle voglio che decidiamo bene come gestire la collezione. Una proposta di base è:
Catalogo Ufficiale (contiene tutte le carte esistenti)
- sets
- cards
Collezione Paolo (Marco.. Giada etc..) contiene solo quelle possedute
- collection
Carta: Charizard ex 125/197
- Quantità: 3
- Lingua: ITA
- Condizione: Near Mint
e una seconda riga (per le carte aggiuntive con lingua diversa)
- Quantità: 1
- Lingua: ENG
- Condizione: Mint
Il DB sarà anche multiutente
Creazione schema database V1
Entriamo in PostgreSQL:
docker exec -it pokemon-postgres psql -U pokemon -d pokemon
Creiamo la Tabella utenti digitando:
CREATE TABLE users (
id BIGSERIAL PRIMARY KEY,
username VARCHAR(50) UNIQUE NOT NULL,
email VARCHAR(255) UNIQUE NOT NULL,
password_hash VARCHAR(255) NOT NULL,
display_name VARCHAR(100),
is_admin BOOLEAN DEFAULT FALSE,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
Per la Tabella Espansioni digitare:
CREATE TABLE sets (
id VARCHAR(50) PRIMARY KEY,
name VARCHAR(255) NOT NULL,
series VARCHAR(255),
release_date DATE,
total_cards INTEGER,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
Per la Tabella Carte digitare:
CREATE TABLE cards (
id VARCHAR(100) PRIMARY KEY,
set_id VARCHAR(50) NOT NULL,
card_number VARCHAR(20),
name VARCHAR(255) NOT NULL,
rarity VARCHAR(100),
supertype VARCHAR(100),
image_small TEXT,
image_large TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT fk_cards_set
FOREIGN KEY (set_id)
REFERENCES sets(id)
);
Per la Tabella Collezioni digitare:
CREATE TABLE collection (
id BIGSERIAL PRIMARY KEY,
user_id BIGINT NOT NULL,
card_id VARCHAR(100) NOT NULL,
quantity INTEGER NOT NULL DEFAULT 1,
language VARCHAR(10) NOT NULL,
condition VARCHAR(30) NOT NULL,
variant VARCHAR(30) NOT NULL,
graded BOOLEAN DEFAULT FALSE,
notes TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT fk_collection_user
FOREIGN KEY (user_id)
REFERENCES users(id),
CONSTRAINT fk_collection_card
FOREIGN KEY (card_id)
REFERENCES cards(id)
);
Per la Tabella Wishlist digitare:
CREATE TABLE wishlist (
id BIGSERIAL PRIMARY KEY,
user_id BIGINT NOT NULL,
card_id VARCHAR(100) NOT NULL,
priority SMALLINT DEFAULT 1,
notes TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT fk_wishlist_user
FOREIGN KEY (user_id)
REFERENCES users(id),
CONSTRAINT fk_wishlist_card
FOREIGN KEY (card_id)
REFERENCES cards(id)
);
Per la Tabella Indici digitare:
CREATE INDEX idx_cards_name
ON cards(name);
CREATE INDEX idx_cards_set
ON cards(set_id);
CREATE INDEX idx_collection_user
ON collection(user_id);
CREATE INDEX idx_collection_card
ON collection(card_id);
Primo utente amministratore:
INSERT INTO users (
username,
email,
password_hash,
display_name,
is_admin
)
VALUES (
'paolo',
'paolo@local',
'TEMP',
'Paolo',
TRUE
);
Verifica finale:
\dt
Dovresti vedere:
cards
collection
sets
users
wishlist
Per verifica l'utente:
SELECT * FROM users;
Prossimo passo: Backend FastAPI
/opt/pokemon
├── backend
├── frontend
├── postgres
├── imports
└── docker-compose.yml
Creiamo il backend:
cd /opt/pokemon/backend
mkdir app
cd app
touch main.py
Verifica: (se non hai tree: apt install tree -y)
tree /opt/pokemon/backend
Dovresti vedere:
backend
└── app
└── main.py
Esegui:
mkdir -p /opt/pokemon/backend/app
touch /opt/pokemon/backend/app/main.py
Adesso creiamo il primo backend minimale e lo facciamo girare in Docker. Passo 1: file Python
nano /opt/pokemon/backend/app/main.py
e inserisci:
from fastapi import FastAPI
app = FastAPI(
title="Pokemon Collection API"
)
@app.get("/")
def root():
return {
"app": "Pokemon Collection",
"status": "running"
}
@app.get("/health")
def health():
return {
"status": "ok"
}
Salva ed esci con CTRL X e poi Y e invio
nano /opt/pokemon/backend/requirements.txt
e inserisci:
fastapi
uvicorn[standard]
Salva ed esci con CTRL X e poi Y e invio
nano /opt/pokemon/backend/Dockerfile
e inserisci:
FROM python:3.13-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY app ./app
CMD ["uvicorn", "app.main:app", "--host", "0.0.0.0", "--port", "8000"]
Salva ed esci con CTRL X e poi Y e invio
nano /opt/pokemon/docker-compose.yml
e sostituisci il contenuto con:
services:
postgres:
image: postgres:17
container_name: pokemon-postgres
restart: unless-stopped
environment:
POSTGRES_DB: pokemon
POSTGRES_USER: pokemon
POSTGRES_PASSWORD: pokemon123
ports:
- "5432:5432"
volumes:
- ./postgres:/var/lib/postgresql/data
api:
build: ./backend
container_name: pokemon-api
restart: unless-stopped
ports:
- "8000:8000"
depends_on:
- postgres
Salva ed esci con CTRL X e poi Y e invio
Build e avvio
cd /opt/pokemon
docker-compose up -d --build
partirà la compilazione, se è andato tutto bene vedrai:
Container pokemon-postgres Running Container pokemon-api Started
Per ulteriore verifixa comando:
curl http://localhost:8000/health
la risposta dovrebbe essere {"status":"ok"}
Se tutto funziona sarà già visibile l'accesso web al db dall'indirizzo http://ip vm:8000/docs dove ip vm è l'indirizzo ip della tua virtual machine o PC, dovrebbe comparire la pagina "Pokemon Collection API"
Creiamo il file database.py
nano /opt/pokemon/backend/app/database.py
con dentro:
from sqlalchemy import create_engine
DATABASE_URL = "postgresql://pokemon:pokemon123@postgres:5432/pokemon"
engine = create_engine(DATABASE_URL)
Salva ed esci con CTRL X e poi Y e invio
Creiamo il file requirements.txt
nano /opt/pokemon/backend/requirements.txt
Sostituisci il contenuto con:
fastapi
uvicorn[standard]
sqlalchemy
psycopg2-binary
Ricostruisci il container
cd /opt/pokemon
docker-compose down
docker-compose build --no-cache
docker-compose up -d
Verifichiamo aprendo la pagina web http://vm ip:8000/db dovrebbe comparire {"users":1}
Creiamo le API reali
nano /opt/pokemon/backend/app/main.py
Aggiungi in calce:
@app.get("/users")
def get_users():
with engine.connect() as conn:
result = conn.execute(
text("""
SELECT
id,
username,
display_name,
is_admin
FROM users
ORDER BY id
""")
)
rows = result.mappings().all()
return rows
@app.get("/sets")
def get_sets():
with engine.connect() as conn:
result = conn.execute(
text("""
SELECT *
FROM sets
ORDER BY name
""")
)
rows = result.mappings().all()
return rows
@app.get("/cards")
def get_cards():
with engine.connect() as conn:
result = conn.execute(
text("""
SELECT *
FROM cards
ORDER BY name
LIMIT 100
""")
)
rows = result.mappings().all()
return rows
Salva ed esci con CTRL X e poi Y e invio
Ricostruzione solita con:
cd /opt/pokemon
docker-compose up -d --build
finita la ricompilazione dovrebbe dare nuovamente "Runnig" e "Started" Verifichiamo aprendo la pagina web http://vm ip:8000/db dovrebbe comparire:
[
{
"id": 1,
"username": "paolo",
"display_name": "Paolo",
"is_admin": true
}
]
<—– Articolo Parte 2 | Articolo Parte 4 —–>