SQLModel
SQLModel is a Python library that unifies SQLAlchemy's ORM capabilities with Pydantic's data validation, simplifying interaction with SQL databases. It aims to reduce code duplication by using a single class definition for both database models and data schemas. SQLModel is currently in version 0.0.38 and maintains an active development cadence with frequent releases, often including bug fixes, dependency updates, and sometimes breaking changes.
Warnings
- breaking SQLModel versions 0.0.35 and higher require Python 3.10 or later.
- breaking SQLModel version 0.0.31 dropped support for Pydantic v1. It now requires Pydantic v2.
- gotcha SQLModel `Session` objects are not thread-safe and should be created per request/task, typically using a `with Session(engine) as session:` block. Detached objects (queried in one session, then used in another) can lead to unexpected behavior.
- gotcha For asynchronous database operations, you must use `sqlalchemy.ext.asyncio.create_async_engine` and `sqlalchemy.ext.asyncio.AsyncSession` (or `sqlmodel.ext.asyncio.Session` when available). Also, ensure you use an async-compatible database driver (e.g., `aiosqlite` for SQLite, `asyncpg` for PostgreSQL).
- gotcha The `SQLModel.metadata.create_all(engine)` call must be executed *after* all your `SQLModel` classes have been defined. If models are in separate files, ensure they are imported before `create_all` is called.
- gotcha When defining relationships between models (e.g., `Hero` and `Team`), circular import issues can arise with type annotations. Python's runtime cannot resolve these.
Install
-
pip install sqlmodel -
pip install sqlmodel[asyncpg] -
pip install sqlmodel[aiosqlite]
Imports
- SQLModel
from sqlmodel import SQLModel
- Field
from sqlmodel import Field
- Session
from sqlmodel import Session
- create_engine
from sqlmodel import create_engine
- select
from sqlmodel import select
- create_async_engine
from sqlalchemy.ext.asyncio import create_async_engine
- AsyncSession
from sqlalchemy.ext.asyncio import AsyncSession
Quickstart
from typing import Optional
from sqlmodel import Field, Session, SQLModel, create_engine, select
class Hero(SQLModel, table=True):
id: Optional[int] = Field(default=None, primary_key=True)
name: str
secret_name: str
age: Optional[int] = Field(default=None, index=True)
sqlite_file_name = "database.db"
sqlite_url = f"sqlite:///{sqlite_file_name}"
# Or for in-memory:
# sqlite_url = "sqlite://"
engine = create_engine(sqlite_url, echo=True)
def create_db_and_tables():
SQLModel.metadata.create_all(engine)
def create_heroes():
hero_1 = Hero(name="Deadpond", secret_name="Dive Wilson")
hero_2 = Hero(name="Spider-Boy", secret_name="Pedro Parqueador", age=16)
hero_3 = Hero(name="Rusty-Man", secret_name="Tommy Sharp", age=48)
with Session(engine) as session:
session.add(hero_1)
session.add(hero_2)
session.add(hero_3)
session.commit()
session.refresh(hero_1)
session.refresh(hero_2)
session.refresh(hero_3)
print("Created heroes:", hero_1, hero_2, hero_3)
def select_heroes():
with Session(engine) as session:
statement = select(Hero).where(Hero.age >= 18)
results = session.exec(statement)
heroes = results.all()
print("Adult heroes:", heroes)
def main():
create_db_and_tables()
create_heroes()
select_heroes()
if __name__ == "__main__":
main()