To boldly go where no one has gone before

Tag: Tecnologia

Database per collezionisti gratuito Parte 3

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 —–>

Database per collezionisti gratuito Parte 2

Creare la struttura del progetto:

mkdir -p /opt/pokemon
cd /opt/pokemon

verifica con:
pwd

deve mostrare:
/opt/pokemon

Installa quindi Docker-Compose:
apt install -y docker.io docker-compose git curl nano

Poi verifica:
systemctl enable --now docker
systemctl status docker

Dovrebbe comparire:
active (running)

Creare le cartelle che servono:

mkdir postgres backend frontend backups imports
ls -l

Dovresti vedere:
backend
backups
frontend
imports
postgres

Creare il file docker-compose.yml

cd /opt/pokemon
nano docker-compose.yml

Aperto il file inserisci:
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

Per salvare CTRL+X e poi Y e invio

Avvio del database

docker-compose up -d
docker ps

Dovresti vedere qualcosa simile a:
pokemon-postgres
postgres:17
Up

Per verificare PostGres sempre da Root esegui:

docker exec -it pokemon-postgres psql -U pokemon -d pokemon

Se tutto è corretto vedrai:
pokemon=#

digita:
SELECT version();

Vedrai una struttura vuota che però indica che il DB è funzionante e pronto ad essere riempito.

per uscire digita il comando:
q

e torni al prompt PostgreSQL:
pokemon=#

Poi esci da PostgreSQL con il comando:
\q

Abbiamo completato tutta la parte infrastrutturale. Prossimo passo: progettare il database.

<—– Articolo Parte 1 | Articolo Parte 3 —–>

© 2026 Paolo Tuttoilmondo

Tema di Anders Noren — Su ↑