Un ORM (Object-Relational Mapper) consente di interagire con il database usando oggetti Python invece di scrivere query SQL manualmente. In questo articolo vedremo come creare un semplice ORM da zero per PostgreSQL usando la libreria psycopg2
.
Requisiti
Assicurati di avere installato:
- Python 3.7+
- PostgreSQL
- psycopg2
pip install psycopg2
1. Connessione al database
Partiamo creando una classe che gestisce la connessione al database.
import psycopg2
class Database:
def __init__(self, dsn):
self.dsn = dsn
self.conn = psycopg2.connect(dsn)
self.conn.autocommit = True
def execute(self, query, params=None):
with self.conn.cursor() as cur:
cur.execute(query, params)
def fetchall(self, query, params=None):
with self.conn.cursor() as cur:
cur.execute(query, params)
return cur.fetchall()
def fetchone(self, query, params=None):
with self.conn.cursor() as cur:
cur.execute(query, params)
return cur.fetchone()
2. Definizione del modello base
Creiamo ora una classe base per i modelli, da cui erediteranno le tabelle.
class Model:
db = None # Da impostare esternamente
@classmethod
def create_table(cls):
fields = []
for name, type_ in cls.__annotations__.items():
sql_type = 'SERIAL PRIMARY KEY' if name == 'id' else 'TEXT'
fields.append(f"{name} {sql_type}")
table = cls.__name__.lower()
sql = f"CREATE TABLE IF NOT EXISTS {table} ({', '.join(fields)});"
cls.db.execute(sql)
def save(self):
fields = [f for f in self.__annotations__ if f != 'id']
values = [getattr(self, f) for f in fields]
placeholders = ', '.join(['%s'] * len(values))
sql = f"INSERT INTO {self.__class__.__name__.lower()} ({', '.join(fields)}) VALUES ({placeholders}) RETURNING id;"
result = self.db.fetchone(sql, values)
self.id = result[0]
@classmethod
def all(cls):
sql = f"SELECT * FROM {cls.__name__.lower()};"
rows = cls.db.fetchall(sql)
instances = []
for row in rows:
obj = cls()
for key, val in zip(cls.__annotations__.keys(), row):
setattr(obj, key, val)
instances.append(obj)
return instances
3. Esempio di utilizzo
Ora possiamo definire un modello e usarlo per interagire con il database.
class User(Model):
id: int
name: str
email: str
# Connessione al database
db = Database("dbname=test user=postgres password=secret")
Model.db = db
# Creazione tabella
User.create_table()
# Inserimento dati
u = User()
u.name = "Mario Rossi"
u.email = "mario@example.com"
u.save()
# Query
users = User.all()
for user in users:
print(user.id, user.name, user.email)
Conclusione
Abbiamo costruito un ORM minimale che permette di mappare oggetti Python su una tabella PostgreSQL. Sebbene molto semplice, questo approccio dimostra i concetti fondamentali dietro gli ORM come SQLAlchemy o Django ORM. Si consiglia di estendere questa base con supporto per tipi di dati più precisi, vincoli, relazioni e validazioni.