一. 什么是ORM?
ORM(Object-Relational Mapping,对象关系映射)是一种用于操作数据库的编程技术,用来在面向对象编程语言 与关系型数据库之间建立映射关系。
通过 ORM,开发者可以使用 Python 对象的方式操作数据库,而无需直接编写 SQL 语句。
举个例子,如果现在要创建一个名为 book 的表,可以直接在 python 代码中这么写:
pythonclass Book(Base): __tablename__ = "book"相当于 SQL 语句:
sqlCREATE TABLE book (...);
**使用 ORM 的优势:**减少重复 SQL 代码;代码更简洁、可读性更高;提高开发效率;自动管理数据库连接和事务;使用参数化机制,降低 SQL 注入风险。
Python 中常见的 ORM 工具 有:SQLAlchemy ORM(最常用、最灵活)、Django ORM(适合 Django 框架)和 Tortoise ORM(异步 ORM)。
后续内容使用的是:SQLAlchemy ORM(社区完善、功能强大、应用最广)
二、快速开始
1、安装ORM
可以直接使用以下命令在 terminal 里面安装:
bash
pip install sqlalchemy[asyncio] aiomysql
2、创建数据库
在MySQL中创建数据库(MySQL 语句/关键字不区分大小写)。首先保证自己已经安装了MySQL并在环境变量里添加了相关路径。打开 cmd,在命令行中输入:
bash
mysql -u root -p
输入密码后进入 MySQL。使用以下命令创建数据库:
sql
CREATE DATABASE 数据库名;
使用以下命令查看数据库是否创建成功:
sql
show databases;

3、创建数据库引擎
数据库引擎所需的 URL 是根据实际使用的数据库、用户名密码、IP地址、端口和数据库名拼写出来的,数据库 URL 的拼写规则为:
python
mysql+aiomysql://用户名:密码@IP地址:端口/数据库名
MySQL数据库url地址写法be like:
pythonDATABASE_URL = "mysql+aiomysql://用户名:密码@数据库IP:3306/数据库名"PostgreSQLurl地址写法be like:
pythonDATABASE_URL = "postgresql+asyncpg://user:password@127.0.0.1:5432/dbname"SQLite数据库url地址写法be like:
pythonDATABASE_URL = "sqlite+aiosqlite:///./test.db"
实际代码:
python
from sqlalchemy.ext.asyncio import create_async_engine
# 创建异步引擎
DATABASE_URL = "mysql+aiomysql://root:123456@localhost:3306/fastapi01?charset=utf8mb4"
async_engine = create_async_engine(
DATABASE_URL, # 数据库URL
echo=True, # 输出SQL日志
pool_size=10, # 连接池中长期保留的连接数量,相当于一直待命的连接
max_overflow=20 # 连接池临时多开的连接数量,用完会释放
)
4、创建表
4.1、定义基类(来定义是啥表?有啥字段?是啥类型?)
先有基类,基类放通用的属性和字段(放所有表都需要的属性/字段)。
python
import datetime
from sqlalchemy import DateTime, func
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column
# 创建表------定义基类
class Base(DeclarativeBase):
create_time: Mapped[datetime.datetime] = mapped_column(
DateTime,
insert_default=func.now(),
default=func.now, # 注意这里只传递函数
comment="创建时间",
)
update_time: Mapped[datetime.datetime] = mapped_column(
DateTime,
insert_default=func.now(),
default=func.now, # 注意这里只传递函数
onupdate=func.now(),
comment="修改时间",
)
DeclarativeBase 让类具备 被 ORM 映射为数据库表的能力。而在 Base 中定义了两个字段:create_time 和 update_time,这两个字段相当于两个默认字段,凡是 Base 的子类都会具备这两个字段。以 create_time 为例,通过结构:
python
字段名:Mapped[python层面的数据类型] = mapped_column(
数据库层面的数据类型,
insert_default = 数据库层面要填入的默认值,
default = python层面的默认值,
comment = "对这个字段的说明"
)
来定义一个字段,可以看到其实 ORM 像是一个"翻译",它提供了 Mapped 类型标注和 mapped_column() 函数,既负责 python 层面的注释,也负责数据库层面的注释,相当于同时告诉 python 和数据库这个字段是什么类型的,它的默认值是什么。
但是为什么要同时在 python 层面和数据库层面同时进行注释呢?这是因为如果只在 Python 层定义 default,那么通过 ORM 插入数据是正常的,但如果直接使用 SQL 操作数据库,默认值不会生效,可能导致数据为空;如果只在数据库层定义 insert_default,那么无论 ORM 还是 SQL 插入,数据库中的数据都是完整的,但在 ORM 操作过程中,Python 对象在提交前可能无法获取该字段的值。
而 sqlalchemy 提供了 DateTime 和 func 等数据库层面的数据类型和操作函数。值得注意的一点是:在 default 中只传递了函数 func.now,而非函数运行的结果 func.now(),这是因为如果写成 default = func.now() 的话,default 的值就被固定为程序启动/加载时的时间,所以,以后不管新增多少数据,create_time 或者 update_time 都是是同一个固定值。
4.2、定义模型类(每一个具体表格都是一个模型类)
模型类就是要定义的具体表格(每一个具体表格都是一个模型类)。比如现在创建一个名为book的表格:
python
from sqlalchemy import String, Float
# 创建表------定义模型类
class Book(Base):
__tablename__ = 'book'
id: Mapped[int] = mapped_column(primary_key=True, comment='书籍id')
bookname: Mapped[str] = mapped_column(String(255), comment='书名')
author: Mapped[str] = mapped_column(String(255), comment='作者')
price: Mapped[float] = mapped_column(Float, comment='价格')
publisher: Mapped[String] = mapped_column(String(255), comment='出版社')
由上述代码可知,模型类的定义跟基类定义的格式非常相似。在模型类定义中,表名用 tablename 字段定义,参数 primary_key 可设置指定字段为主键,String 是 sqlalchemy 提供的数据库层面的数据类型,String(255) 意味着数据库字段类型是字符串(VARCHAR),最大长度为 255 个字符。
5、启动时自动建表
当所需要的表格已经定义好,最后需要通过 lifespan 机制,在 FastAPI 启动时自动执行建表函数,确保数据库表在接口使用前已经准备好。代码如下:
python
# 启动时自动建表
# 定义建表函数
# 注意Base.metadata.create_all后面没有小括号
async def create_tables():
async with async_engine.begin() as conn:
await conn.run_sync(Base.metadata.create_all)
# 定义FastAPI启动时异步执行建表函数create_tables()
@asynccontextmanager
async def lifespan(app: FastAPI):
await create_tables()
yield
# 将上面自定义的lifespan函数传递给FastAPI的lifespan函数(FastAPI有一个自己的lifespan函数)
app = FastAPI(lifespan=lifespan)
点击run查看有无报错,运行成功会出现如下提示:

如果没报错可以看看 pycharm 里面的 database 里的表格情况:


填写好 user 和 password,点击 ok。就能在右侧边栏的 Database 中看到在 fastapi01 里面创建的 book 表格了:

6、完整代码
python
# 1.导包
import datetime
from contextlib import asynccontextmanager
from fastapi import FastAPI
from sqlalchemy.ext.asyncio import create_async_engine
from sqlalchemy.orm import DeclarativeBase, Mapped, mapped_column
from sqlalchemy import DateTime, func, String, Float
# 2.根据自己使用的数据库构造数据库URL
ASYNC_DATABASE_URL = "mysql+aiomysql://root:123456@localhost:3306/fastapi01?charset=utf8mb4"
# 3.构造数据库引擎,并把构造好的URL传递进去
async_engine = create_async_engine(
ASYNC_DATABASE_URL,
echo=True,
pool_size=10,
max_overflow=20,
)
# 4.创建表------定义基类
class Base(DeclarativeBase):
create_time: Mapped[datetime.datetime] = mapped_column(
DateTime,
insert_default=func.now(),
default=func.now,
comment="创建时间",
)
update_time: Mapped[datetime.datetime] = mapped_column(
DateTime,
insert_default=func.now(),
default=func.now,
onupdate=func.now(),
comment="修改时间",
)
# 4.创建表------定义模型类
class Book(Base):
__tablename__ = "book"
id: Mapped[int] = mapped_column(primary_key=True, comment="书籍id")
bookname: Mapped[str] = mapped_column(String(255), comment="书名")
author: Mapped[str] = mapped_column(String(255), comment="作者")
price: Mapped[float] = mapped_column(Float, comment="价格")
publisher: Mapped[str] = mapped_column(String(255), comment="出版社")
# 5.启动时自动建表------连接数据库 → 执行ORM的建表操作
async def create_tables():
async with async_engine.begin() as conn:
await conn.run_sync(Base.metadata.create_all)
# 5.启动时自动建表------定义FastAPI启动和关闭时执行的代码
@asynccontextmanager
async def lifespan(app: FastAPI):
await create_tables()
yield
# 5.启动时自动建表------将上面自定义的lifespan赋给FastAPI中特定的lifespan函数
app = FastAPI(lifespan=lifespan)
@app.get("/")
async def root():
return {"message": "Hello World"}