From 5fa137289c5e6b506d19a8d99f3528bb66cbc713 Mon Sep 17 00:00:00 2001 From: Alexander Valenchits Date: Sat, 25 Apr 2026 13:11:25 +0000 Subject: [PATCH] docs(readme): add DB connection examples for PostgreSQL/MySQL/MSSQL across ORMs Adds a 'Database connection examples' section that documents: - A driver matrix (PostgreSQL / MySQL / MSSQL) for SQLAlchemy sync/async, Tortoise, and Peewee, including which packages to pip install and which ODBC driver MSSQL needs. - SQLAlchemy: pip commands and .env URL examples for psycopg/asyncpg (Postgres), pymysql/aiomysql (MySQL), pyodbc/aioodbc (MSSQL with ODBC Driver 18). Notes that the adapter auto-converts sync URLs to async for Postgres/MySQL/SQLite, and that MSSQL needs an explicit SQLALCHEMY_ASYNC_DATABASE_URL. - Tortoise: pip + .env + a runnable startup/shutdown snippet that uses Tortoise.init / Tortoise.generate_schemas / close_connections via attributes the adapter exposes (database_url, app_label, models). Calls out that Tortoise has no MSSQL driver. - Peewee: pip + .env + viewset wiring. Calls out that this adapter's URL parser only supports sqlite/postgres/mysql, no MSSQL. - A 'Building the adapter from code (no env vars)' snippet using ORMFactory.create_adapter for users who avoid environment variables. Documentation only \u2014 no code changes. --- README.md | 209 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 209 insertions(+) diff --git a/README.md b/README.md index 50bc3c6..a840339 100644 --- a/README.md +++ b/README.md @@ -46,6 +46,215 @@ pip install "fastapi-viewsets[test]" # pytest, httpx, coverage, etc. For async SQLAlchemy you still need a driver such as `aiosqlite`, `asyncpg`, or `aiomysql` alongside your database URL. +## Database connection examples + +`fastapi-viewsets` doesn't bundle DB drivers — you pick them per stack. +The table below maps each ORM to the install command, the driver(s) +you need, and the URL shape for **PostgreSQL**, **MySQL**, and +**Microsoft SQL Server**. + +| ORM | PostgreSQL | MySQL | MSSQL | +| --- | --- | --- | --- | +| **SQLAlchemy (sync)** | `psycopg[binary]` or `psycopg2-binary` | `pymysql` or `mysqlclient` | `pyodbc` + ODBC Driver 17/18 | +| **SQLAlchemy (async)** | `asyncpg` | `aiomysql` or `asyncmy` | `aioodbc` + ODBC Driver 17/18 | +| **Tortoise ORM** | `asyncpg` (built-in) | `aiomysql` (built-in) | Not supported by Tortoise | +| **Peewee** | `psycopg2-binary` | `pymysql` or `mysqlclient` | Not supported by this adapter | + +> The `SQLAlchemyAdapter` auto-converts a sync URL to its async +> counterpart (`postgresql://` → `postgresql+asyncpg://`, +> `mysql://` → `mysql+aiomysql://`, `sqlite:///` → +> `sqlite+aiosqlite:///`). For MSSQL you have to set the async URL +> explicitly via `SQLALCHEMY_ASYNC_DATABASE_URL`. If the matching async +> driver is not installed, `SQLAlchemyAdapter` falls back to sync-only +> mode and `get_async_session()` raises a helpful `RuntimeError` +> (since v1.2.1). + +The library reads database configuration from environment variables +(loaded via `python-dotenv` from `.env`). Pick the ORM with `ORM_TYPE`, +then set the URL with `_DATABASE_URL` (or the generic +`DATABASE_URL`). + +### SQLAlchemy (sync and async) + +```bash +# PostgreSQL +pip install "fastapi-viewsets[sqlalchemy]" "psycopg[binary]" asyncpg + +# MySQL +pip install "fastapi-viewsets[sqlalchemy]" pymysql aiomysql + +# MSSQL (needs Microsoft ODBC Driver 17 or 18 on the host) +pip install "fastapi-viewsets[sqlalchemy]" pyodbc aioodbc +``` + +Example `.env` (one block at a time): + +```dotenv +# --- PostgreSQL --- +ORM_TYPE=sqlalchemy +SQLALCHEMY_DATABASE_URL=postgresql+psycopg://user:pass@db.example.com:5432/app +# Optional explicit async URL; otherwise auto-derived to postgresql+asyncpg:// +SQLALCHEMY_ASYNC_DATABASE_URL=postgresql+asyncpg://user:pass@db.example.com:5432/app + +# --- MySQL --- +ORM_TYPE=sqlalchemy +SQLALCHEMY_DATABASE_URL=mysql+pymysql://user:pass@db.example.com:3306/app?charset=utf8mb4 +SQLALCHEMY_ASYNC_DATABASE_URL=mysql+aiomysql://user:pass@db.example.com:3306/app?charset=utf8mb4 + +# --- MSSQL --- +ORM_TYPE=sqlalchemy +# URL-encode the ODBC driver name ("+" instead of spaces). +SQLALCHEMY_DATABASE_URL=mssql+pyodbc://user:pass@db.example.com:1433/app?driver=ODBC+Driver+18+for+SQL+Server&Encrypt=yes&TrustServerCertificate=no +SQLALCHEMY_ASYNC_DATABASE_URL=mssql+aioodbc://user:pass@db.example.com:1433/app?driver=ODBC+Driver+18+for+SQL+Server&Encrypt=yes&TrustServerCertificate=no +``` + +Use `BaseViewset` for sync code or `AsyncBaseViewset` for async code +(see the [Async quickstart](#async-quickstart-sqlalchemy-2x--pydantic-v2) +below). + +### Tortoise ORM + +Tortoise is async-only. The adapter takes a database URL plus a list +of model modules to register on startup. + +```bash +# PostgreSQL +pip install "fastapi-viewsets[tortoise]" # pulls in asyncpg + +# MySQL +pip install "fastapi-viewsets[tortoise]" aiomysql +``` + +```dotenv +# --- PostgreSQL --- +ORM_TYPE=tortoise +TORTOISE_DATABASE_URL=postgres://user:pass@db.example.com:5432/app +TORTOISE_MODELS=["app.models"] +TORTOISE_APP_LABEL=models + +# --- MySQL --- +ORM_TYPE=tortoise +TORTOISE_DATABASE_URL=mysql://user:pass@db.example.com:3306/app +TORTOISE_MODELS=["app.models"] +TORTOISE_APP_LABEL=models +``` + +```python +from fastapi import FastAPI +from tortoise import Tortoise + +from fastapi_viewsets import AsyncBaseViewset +from fastapi_viewsets.orm.factory import ORMFactory + +app = FastAPI() +adapter = ORMFactory.get_default_adapter() # built from the env vars above + + +@app.on_event("startup") +async def _init_tortoise() -> None: + """Open the Tortoise connection pool and create schema if needed. + + The adapter also initializes Tortoise lazily on first DB call; + doing it here gives you control over schema creation. + """ + await Tortoise.init( + db_url=adapter.database_url, + modules={adapter.app_label: adapter.models}, + ) + await Tortoise.generate_schemas(safe=True) + + +@app.on_event("shutdown") +async def _close_tortoise() -> None: + """Close the Tortoise connection pool.""" + await Tortoise.close_connections() + + +# Define your Tortoise models in app/models.py and pass them to AsyncBaseViewset. +# from app.models import Item +# from app.schemas import ItemSchema +# items = AsyncBaseViewset( +# endpoint="/items", +# model=Item, +# response_model=ItemSchema, +# db_session=adapter.get_async_session, +# orm_adapter=adapter, +# tags=["items"], +# ) +# items.register(methods=["LIST", "GET", "POST", "PATCH", "DELETE"]) +# app.include_router(items) +``` + +> **MSSQL is not supported by Tortoise ORM.** Use SQLAlchemy with +> `aioodbc` for SQL Server. + +### Peewee + +Peewee is sync-only. The adapter parses the URL and instantiates the +right `Database` class. + +```bash +# PostgreSQL +pip install "fastapi-viewsets[peewee]" psycopg2-binary + +# MySQL +pip install "fastapi-viewsets[peewee]" pymysql +``` + +```dotenv +# --- PostgreSQL --- +ORM_TYPE=peewee +PEEWEE_DATABASE_URL=postgresql://user:pass@db.example.com:5432/app + +# --- MySQL --- +ORM_TYPE=peewee +PEEWEE_DATABASE_URL=mysql://user:pass@db.example.com:3306/app +``` + +```python +from fastapi import FastAPI + +from fastapi_viewsets import BaseViewset +from fastapi_viewsets.orm.factory import ORMFactory + +app = FastAPI() +adapter = ORMFactory.get_default_adapter() # built from the env vars above + +# from app.models import Item # peewee.Model subclass +# from app.schemas import ItemSchema # Pydantic v2 schema +# items = BaseViewset( +# endpoint="/items", +# model=Item, +# response_model=ItemSchema, +# db_session=adapter.get_session, +# orm_adapter=adapter, +# tags=["items"], +# ) +# items.register(methods=["LIST", "GET", "POST", "PATCH", "DELETE"]) +# app.include_router(items) +``` + +> **MSSQL is not supported by this Peewee adapter** (the URL parser +> only handles `sqlite:///`, `postgresql://`, `postgres://`, +> `mysql://`). For SQL Server, use SQLAlchemy. + +### Building the adapter from code (no env vars) + +When you don't want to rely on environment variables, instantiate the +adapter directly and pass it to the viewset via `orm_adapter=`: + +```python +from fastapi_viewsets.orm.factory import ORMFactory + +adapter = ORMFactory.create_adapter( + "sqlalchemy", + { + "database_url": "postgresql+psycopg://user:pass@db.example.com:5432/app", + "async_database_url": "postgresql+asyncpg://user:pass@db.example.com:5432/app", + }, +) +``` + ## Quickstart (SQLAlchemy, sync) Save as `main.py` in an empty folder and run `python main.py` or `uvicorn main:app --reload`.