FastAPI, SQLModel, Alembic, et PostgreSQL

laptop computer showing codes

pip install fastapi uvicorn[standard] sqlalchemy sqlmodel alembic psycopg2-binary
pip freeze > requirements.txt
fastapi_project/

├── app/
│   ├── __init__.py
│   ├── main.py
│   ├── database.py
│   ├── models.py
│   ├── crud.py
│   └── alembic/
│       └── (répertoire de migration Alembic)
├── alembic.ini
├── requirements.txt
└── .env

app/database.py

import os
from sqlmodel import SQLModel, create_engine
from sqlalchemy.orm import sessionmaker
from dotenv import load_dotenv

load_dotenv()

DATABASE_URL = os.getenv("DATABASE_URL")
engine = create_engine(DATABASE_URL)

SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)

def init_db():
    SQLModel.metadata.create_all(bind=engine)

app/models.py

from sqlmodel import SQLModel, Field
from typing import Optional

class User(SQLModel, table=True):
    id: Optional[int] = Field(default=None, primary_key=True)
    email: str
    hash_password: str
    country: str
    level: str  # Évitez d'utiliser des mots réservés comme "level"

app/crud.py

from sqlalchemy.orm import Session
from app.models import User

def create_user(db: Session, user: User):
    db.add(user)
    db.commit()
    db.refresh(user)
    return user

def get_user(db: Session, user_id: int):
    return db.query(User).filter(User.id == user_id).first()

app/main.py

from fastapi import FastAPI, Depends, HTTPException
from sqlalchemy.orm import Session
from app.database import SessionLocal, init_db
from app.models import User
from app.crud import create_user, get_user

app = FastAPI()

@app.on_event("startup")
def on_startup():
    init_db()

def get_db():
    db = SessionLocal()
    try:
        yield db
    finally:
        db.close()

@app.post("/users/", response_model=User)
def create_new_user(user: User, db: Session = Depends(get_db)):
    return create_user(db=db, user=user)

@app.get("/users/{user_id}", response_model=User)
def read_user(user_id: int, db: Session = Depends(get_db)):
    db_user = get_user(db, user_id=user_id)
    if db_user is None:
        raise HTTPException(status_code=404, detail="User not found")
    return db_user

Exécute cette commande pour initialiser Alembic :

alembic init alembic

Ensuite, modifie alembic.ini pour pointer vers le fichier .env :

sqlalchemy.url = postgresql://username:password@localhost/dbname

Ensuite, dans alembic/env.py, importe les modèles de SQLModel :

from sqlmodel import SQLModel
from app.database import engine

target_metadata = SQLModel.metadata

def run_migrations_offline():
    url = config.get_main_option("sqlalchemy.url")
    context.configure(
        url=url, target_metadata=target_metadata, literal_binds=True, dialect_opts={"paramstyle": "named"},
    )

def run_migrations_online():
    connectable = engine

    with connectable.connect() as connection:
        context.configure(connection=connection, target_metadata=target_metadata)

        with context.begin_transaction():
            context.run_migrations()

Exécute cette commande pour créer une migration :

alembic revision --autogenerate -m "Create users table"

Applique la migration :

alembic upgrade head

Lancer l’application

Utilise Uvicorn pour démarrer le serveur :

uvicorn app.main:app --reload