首页 / FastAPI 入门教程 / 连接数据库与定义模型

FastAPI 入门教程

连接数据库与定义模型

本教程共 50 篇 · 第 33 篇 · 更新于 2026-08-12 · 约 7 分钟阅读

FastAPIFastAPI 入门教程SQLAlchemy数据库连接建表

本节目标:学会用 SQLAlchemy 2.0 连上数据库、定义模型类(也就是表),并真正把表创建到数据库里。代码复制即可运行。

上一章说了 ORM 的概念,这一章动真格。我们要做三件事:建一个「引擎」管连接、定义一个「基类」和若干「模型类」描述表结构、最后让代码自动把表建出来。

33-1 第一步:创建引擎 Engine

引擎(Engine)是 SQLAlchemy 里最底层的对象,它负责管理数据库连接池,所有会话都通过它和数据库说话。创建引擎靠 create_engine,传入一个「连接字符串」即可。

from sqlalchemy import create_engine

# SQLite:数据库就是一个本地文件,无需额外服务
SQLALCHEMY_DATABASE_URL = "sqlite:///./test.db"

engine = create_engine(
    SQLALCHEMY_DATABASE_URL,
    # 仅 SQLite 需要这一行,允许跨线程使用同一连接
    connect_args={"check_same_thread": False},
)

连接字符串的写法是 数据库类型://用户名:密码@地址:端口/库名。SQLite 比较特殊,用 sqlite:///文件相对路径 表示当前目录下的文件。

Note

check_same_thread=False 只在用 SQLite 时加。FastAPI 一个请求可能用到多个线程,SQLite 默认不允许跨线程,加上这行才不会报错。换成 PostgreSQL 等数据库时删掉它。

33-2 连接字符串对照表

不同数据库,连接字符串格式不同。本书只改这一行就能切换数据库:

# SQLite(文件)
"sqlite:///./test.db"

# PostgreSQL
"postgresql://user:password@localhost:5432/mydb"

# MySQL
"mysql+pymysql://user:password@localhost:3306/mydb"

# SQL Server
"mssql+pyodbc://user:password@localhost/mydb?driver=ODBC+Driver+17+for+SQL+Server"

注意 PostgreSQL 和 MySQL 要额外装驱动:pip install psycopg2-binary(PostgreSQL,对应 postgresql+psycopg2:// 前缀)、pip install pymysql(MySQL)。否则连不上。

Tip

真正项目里,连接字符串别写死在代码里。应该放到环境变量或配置文件,避免把密码提交到代码仓库。这里为演示清晰,先直接写。

33-3 定义声明基类 Base

所有模型类都要继承同一个「基类」,SQLAlchemy 才能识别它们、统一管理表结构。2.0 风格用 DeclarativeBase

from sqlalchemy.orm import DeclarativeBase

class Base(DeclarativeBase):
    pass

这个 Base 收集了所有子类的表信息。等会儿建表时,只要对 Base.metadata.create_all(engine) 说一声,它就知道要建哪些表。

33-4 用 Mapped 定义字段

有了 Base,就能写模型类了。每个模型类对应一张表,类里的属性对应表里的列。SQLAlchemy 2.0 推荐用 Mapped[类型]mapped_column()

from sqlalchemy import String, Integer, Boolean
from sqlalchemy.orm import Mapped, mapped_column, relationship

from .database import Base  # 假设 Base 在 database.py

class User(Base):
    __tablename__ = "users"  # 指定表名,不写则默认用类名小写

    id: Mapped[int] = mapped_column(primary_key=True)
    name: Mapped[str] = mapped_column(String(30))
    email: Mapped[str] = mapped_column(String(100), unique=True, index=True)
    is_active: Mapped[bool] = mapped_column(Boolean, default=True)

几个要点:

  • __tablename__ 告诉 SQLAlchemy 这张表叫什么。建议显式写上。
  • Mapped[int] 是类型提示,mapped_column(primary_key=True) 描述列约束。
  • String(30) 里的数字表示最大长度,关系型数据库要求字符串列有长度。
  • unique=True 约束邮箱不能重复;index=True 给这列加索引,按邮箱查会更快。
  • default=True 表示插数据时没给值就填 True。
Note

老教程常写 id = Column(Integer, primary_key=True)。这是 1.x 写法,在 2.0 仍能用,但本书统一用 Mapped[...] + mapped_column,类型更明确,编辑器支持更好。

33-5 建表:Base.metadata.create_all

模型和引擎都就绪后,调用一次就能把表真正建到数据库文件里:

from .database import engine, Base
import models  # 确保模型类被导入,Base 才认识它们

Base.metadata.create_all(engine)

注意 import models 很关键。只有模型类被 Python 加载过,Base 才记录下它们的表结构,否则建出来的库是空的。

生产环境其实更推荐用 Alembic 做数据库迁移(记录每次表结构变更),而不是直接 create_all。但学习阶段 create_all 最简单直观。

还有一个新手极容易困惑的行为要说清楚:create_all 只会创建「尚不存在」的表,对已经存在的表它什么都不做。也就是说,你给模型加了一个新字段再运行一次,数据库里的表结构不会跟着变,程序却会在查询时报「找不到该列」。学习阶段最省事的办法是直接删掉 test.db 文件让它重建,但这会丢失全部数据;真实项目里就必须靠 Alembic 生成迁移脚本,一步步把表结构演进记录下来。

33-6 会话 Session 与 sessionmaker

引擎管连接,但真正「增删改查」靠的是会话(Session)。你可以把 Session 理解成「一次和数据库对话的笔记本」:你先把改动记在笔记本上,最后 commit() 才真正写入。

sessionmaker 用来批量生产 Session 实例:

from sqlalchemy.orm import sessionmaker

SessionLocal = sessionmaker(bind=engine, autoflush=False, autocommit=False)
  • bind=engine:这些会话都通过上面那个引擎连库。
  • autocommit=False:不自动提交,必须手动 commit()
  • autoflush=False:不在查询前自动刷写,行为更可控。

用的时候拿一个会话,用完关闭:

session = SessionLocal()
try:
    # 在这里做数据库操作
    session.commit()
finally:
    session.close()
Tip

在 FastAPI 里,我们通常用「带 yield 的依赖」给每个请求发一个独立会话,请求结束自动关。这招第 34 章细讲,先知道 Session 要「用完即关」。

33-7 异步版本:create_async_engine

第 36 章才深入异步,但这里先给出对应关系,方便你提前了解。异步项目要用 create_async_engineasync_sessionmaker

from sqlalchemy.ext.asyncio import create_async_engine, async_sessionmaker

# 注意协议是 asyncpg,不是普通的 postgresql
SQLALCHEMY_DATABASE_URL = "postgresql+asyncpg://user:password@localhost:5432/mydb"

async_engine = create_async_engine(SQLALCHEMY_DATABASE_URL)
AsyncSessionLocal = async_sessionmaker(async_engine, expire_on_commit=False)

模型类的定义和同步版一模一样,不用改。区别只在「连库方式」和「操作时要 await」。

33-8 完整可跑示例

把上面几点拼成一个最小可运行文件 main.py(SQLite 版)。这里先只演示连库和建表,接口留到第 34 章:

from sqlalchemy import String, Boolean, create_engine
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column, sessionmaker

# 1. 引擎
engine = create_engine(
    "sqlite:///./test.db",
    connect_args={"check_same_thread": False},
)

# 2. 基类
class Base(DeclarativeBase):
    pass

# 3. 模型
class User(Base):
    __tablename__ = "users"

    id: Mapped[int] = mapped_column(primary_key=True)
    name: Mapped[str] = mapped_column(String(30))
    email: Mapped[str] = mapped_column(String(100), unique=True)

# 4. 建表
Base.metadata.create_all(engine)

# 5. 会话工厂
SessionLocal = sessionmaker(bind=engine, autoflush=False, autocommit=False)

print("数据库与表已就绪")

运行 python main.py,目录下会多出一个 test.db 文件,里面已有空的 users 表。你可以用 DB Browser for SQLite 这类工具打开查看。

换成别的数据库时,代码里唯一需要改的就是连接字符串。它的格式统一为「数据库类型+驱动名://用户名:密码@主机:端口/库名」,比如 MySQL 用 mysql+pymysql://user:pass@localhost:3306/mydb,PostgreSQL 用 postgresql+psycopg2://user:password@localhost:5432/mydb。SQLite 是文件型数据库,没有账号和主机,所以写成 sqlite:///./test.db 这种相对路径形式。模型定义、会话用法完全不受影响,这正是 ORM 屏蔽数据库差异带来的便利。

Note

连库、定义模型、建表这三步是一次性的「准备工作」。之后每次操作数据,都是「开会话 → 操作 → 提交/关闭」。下一章我们就把增删改查一个个写出来。