pip install sqlmodel★Pulls in SQLAlchemy 2.0 and Pydantic v2 automatically — you don't install them separately.from sqlmodel import (SQLModel, Field, Relationship, Session, create_engine, select)★The whole everyday surface.SQLModelre-exports most of what you need; drop to rawsqlalchemyonly for advanced column types.col: str = Field(sa_column=Column(JSON)) # escape hatchAnything SQLAlchemy can do, you can reach viasa_column=/sa_type=— SQLModel is a thin, typed layer, not a walled garden.
class Hero(SQLModel, table=True): id: int | None = Field(default=None, primary_key=True) name: str age: int | None = None★table=True makes it a real DB table. Annotations become columns;| None= nullable; a default makes it optional. The class is also a valid Pydantic model.class HeroBase(SQLModel): # NO table=True name: str★Withouttable=Trueit's a plain Pydantic schema (request/response body, shared base) — no table, full validation. This distinction drives the whole design.__tablename__ = "heroes" # override table nameDefault table name is the lowercased class name. Set__tablename__to override.
id: int | None = Field(default=None, primary_key=True)★The canonical PK: optional int,Noneuntil the DB assigns it on commit.name: str = Field(index=True) email: str = Field(unique=True, index=True)★index=Truebuilds a DB index;unique=Truea unique constraint. Combine freely.team_id: int | None = Field(default=None, foreign_key="team.id")★Foreign key as a string"table.column". This is the column; theRelationship(card 7) is the navigation attribute.name: str = Field(max_length=255, nullable=False) created: datetime = Field(default_factory=datetime.utcnow)max_lengthsets the VARCHAR limit (Pydantic v2);default_factoryfor per-row computed defaults.